@transcend-io/cli 12.0.0 → 12.1.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 +160 -149
- package/dist/{app-CR3U95JW.mjs → app-CfJlhIn8.mjs} +18 -18
- package/dist/app-CfJlhIn8.mjs.map +1 -0
- package/dist/{approvePrivacyRequests-BDG82nlp.mjs → approvePrivacyRequests-BWj6SsgX.mjs} +2 -2
- package/dist/{approvePrivacyRequests-BDG82nlp.mjs.map → approvePrivacyRequests-BWj6SsgX.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-1I3xKR1X.mjs +2 -0
- package/dist/buildExamples-1I3xKR1X.mjs.map +1 -0
- package/dist/{buildXdiSyncEndpoint-FExEagKc.mjs → buildXdiSyncEndpoint-CAal3Xc4.mjs} +2 -2
- package/dist/{buildXdiSyncEndpoint-FExEagKc.mjs.map → buildXdiSyncEndpoint-CAal3Xc4.mjs.map} +1 -1
- package/dist/{bulkRestartRequests-OKwKXc52.mjs → bulkRestartRequests-B3wupylo.mjs} +2 -2
- package/dist/{bulkRestartRequests-OKwKXc52.mjs.map → bulkRestartRequests-B3wupylo.mjs.map} +1 -1
- package/dist/{bulkRetryEnrichers-Dl-n8AFF.mjs → bulkRetryEnrichers-D801tBcB.mjs} +2 -2
- package/dist/{bulkRetryEnrichers-Dl-n8AFF.mjs.map → bulkRetryEnrichers-D801tBcB.mjs.map} +1 -1
- package/dist/{cancelPrivacyRequests-BIgX5SDz.mjs → cancelPrivacyRequests-CAKTZF47.mjs} +2 -2
- package/dist/{cancelPrivacyRequests-BIgX5SDz.mjs.map → cancelPrivacyRequests-CAKTZF47.mjs.map} +1 -1
- package/dist/{command-X7lkPELf.mjs → command-BDQOL_MQ.mjs} +2 -2
- package/dist/{command-X7lkPELf.mjs.map → command-BDQOL_MQ.mjs.map} +1 -1
- package/dist/{constants-BRsj50Nb.mjs → constants-BlKpAm3R.mjs} +2 -2
- package/dist/{constants-BRsj50Nb.mjs.map → constants-BlKpAm3R.mjs.map} +1 -1
- package/dist/{createPoolingCommandUi-CyPZ3GqN.mjs → createPoolingCommandUi-JBOW5UxY.mjs} +2 -2
- package/dist/{createPoolingCommandUi-CyPZ3GqN.mjs.map → createPoolingCommandUi-JBOW5UxY.mjs.map} +1 -1
- package/dist/{downloadPrivacyRequestFiles-EBySRnvd.mjs → downloadPrivacyRequestFiles-Cf14MQR4.mjs} +2 -2
- package/dist/{downloadPrivacyRequestFiles-EBySRnvd.mjs.map → downloadPrivacyRequestFiles-Cf14MQR4.mjs.map} +1 -1
- package/dist/{generateCrossAccountApiKeys-pWTrIUE6.mjs → generateCrossAccountApiKeys-CVdsJFOe.mjs} +2 -2
- package/dist/{generateCrossAccountApiKeys-pWTrIUE6.mjs.map → generateCrossAccountApiKeys-CVdsJFOe.mjs.map} +1 -1
- package/dist/{impl-1SsreZWo.mjs → impl-2sYR_UFO.mjs} +2 -2
- package/dist/{impl-1SsreZWo.mjs.map → impl-2sYR_UFO.mjs.map} +1 -1
- package/dist/impl-B-Oe7DcE2.mjs +421 -0
- package/dist/{impl-DQcgEx7V2.mjs.map → impl-B-Oe7DcE2.mjs.map} +1 -1
- package/dist/{impl-CspH2ZWx.mjs → impl-BD03bzB-.mjs} +2 -2
- package/dist/{impl-CspH2ZWx.mjs.map → impl-BD03bzB-.mjs.map} +1 -1
- package/dist/{impl-CrdqsTUy.mjs → impl-BGtup1F9.mjs} +2 -2
- package/dist/{impl-CrdqsTUy.mjs.map → impl-BGtup1F9.mjs.map} +1 -1
- package/dist/impl-BQRP9NIv.mjs +2 -0
- package/dist/impl-BQRP9NIv.mjs.map +1 -0
- package/dist/{impl-CWP6AXSb.mjs → impl-BQe6vddz.mjs} +2 -2
- package/dist/{impl-CWP6AXSb.mjs.map → impl-BQe6vddz.mjs.map} +1 -1
- package/dist/{impl-B440iuvL.mjs → impl-BQj1Ien4.mjs} +3 -3
- package/dist/{impl-B440iuvL.mjs.map → impl-BQj1Ien4.mjs.map} +1 -1
- package/dist/{impl-CU38kdj8.mjs → impl-BRFL8xp7.mjs} +2 -2
- package/dist/{impl-CU38kdj8.mjs.map → impl-BRFL8xp7.mjs.map} +1 -1
- package/dist/{impl-eeS2lVi5.mjs → impl-BTvZxws5.mjs} +2 -2
- package/dist/{impl-eeS2lVi5.mjs.map → impl-BTvZxws5.mjs.map} +1 -1
- package/dist/{impl-D1UogVAq.mjs → impl-BVcy_GYa.mjs} +2 -2
- package/dist/{impl-D1UogVAq.mjs.map → impl-BVcy_GYa.mjs.map} +1 -1
- package/dist/{impl-vmLq6HCI.mjs → impl-B_CRtRo8.mjs} +2 -2
- package/dist/{impl-vmLq6HCI.mjs.map → impl-B_CRtRo8.mjs.map} +1 -1
- package/dist/{impl-7rdGuHOC.mjs → impl-BgD7TeMD.mjs} +2 -2
- package/dist/{impl-7rdGuHOC.mjs.map → impl-BgD7TeMD.mjs.map} +1 -1
- package/dist/{impl-BdTxFv63.mjs → impl-BgkovGOz.mjs} +2 -2
- package/dist/{impl-BdTxFv63.mjs.map → impl-BgkovGOz.mjs.map} +1 -1
- package/dist/{impl-Do2TNA3-.mjs → impl-Bh02zrTq.mjs} +2 -2
- package/dist/{impl-Do2TNA3-.mjs.map → impl-Bh02zrTq.mjs.map} +1 -1
- package/dist/{impl-HUfaARep.mjs → impl-BopWGXnH.mjs} +2 -2
- package/dist/{impl-HUfaARep.mjs.map → impl-BopWGXnH.mjs.map} +1 -1
- package/dist/{impl-iD6Fknfe.mjs → impl-BxvKe9_7.mjs} +2 -2
- package/dist/{impl-iD6Fknfe.mjs.map → impl-BxvKe9_7.mjs.map} +1 -1
- package/dist/{impl-DtnYcktN.mjs → impl-BzIyVAg_.mjs} +2 -2
- package/dist/{impl-DtnYcktN.mjs.map → impl-BzIyVAg_.mjs.map} +1 -1
- package/dist/{impl-Cxprr2hw.mjs → impl-C-PvYlFE.mjs} +2 -2
- package/dist/{impl-Cxprr2hw.mjs.map → impl-C-PvYlFE.mjs.map} +1 -1
- package/dist/impl-C36c5NfN.mjs +2 -0
- package/dist/impl-C36c5NfN.mjs.map +1 -0
- package/dist/{impl-B7l4bLG5.mjs → impl-CC-yFYWt.mjs} +2 -2
- package/dist/{impl-B7l4bLG5.mjs.map → impl-CC-yFYWt.mjs.map} +1 -1
- package/dist/{impl-CudSYx-g.mjs → impl-CFB_508q.mjs} +2 -2
- package/dist/{impl-CudSYx-g.mjs.map → impl-CFB_508q.mjs.map} +1 -1
- package/dist/{impl-DxQ9yj3H.mjs → impl-CG3f4B6u.mjs} +2 -2
- package/dist/{impl-DxQ9yj3H.mjs.map → impl-CG3f4B6u.mjs.map} +1 -1
- package/dist/{impl-DqHEIzpj.mjs → impl-CVUD-7mJ.mjs} +5 -5
- package/dist/{impl-DqHEIzpj.mjs.map → impl-CVUD-7mJ.mjs.map} +1 -1
- package/dist/{impl-BWPPVXG3.mjs → impl-CoqCyj7e.mjs} +3 -3
- package/dist/{impl-BWPPVXG3.mjs.map → impl-CoqCyj7e.mjs.map} +1 -1
- package/dist/{impl-BLQDgwxL.mjs → impl-Cq-GARM1.mjs} +2 -2
- package/dist/{impl-BLQDgwxL.mjs.map → impl-Cq-GARM1.mjs.map} +1 -1
- package/dist/{impl-DQG1pMzD2.mjs → impl-Cvyc-iTI2.mjs} +2 -2
- package/dist/{impl-DQG1pMzD2.mjs.map → impl-Cvyc-iTI2.mjs.map} +1 -1
- package/dist/impl-CwBa15M42.mjs +3 -0
- package/dist/impl-CwBa15M42.mjs.map +1 -0
- package/dist/{impl-CieztjdM.mjs → impl-D3gqip90.mjs} +2 -2
- package/dist/{impl-CieztjdM.mjs.map → impl-D3gqip90.mjs.map} +1 -1
- package/dist/{impl-DcL-8NEa2.mjs → impl-D6Lzwd-R2.mjs} +2 -2
- package/dist/{impl-DcL-8NEa2.mjs.map → impl-D6Lzwd-R2.mjs.map} +1 -1
- package/dist/impl-DG-041kN.mjs +3 -0
- package/dist/impl-DG-041kN.mjs.map +1 -0
- package/dist/{impl-DXbxO2VJ.mjs → impl-DTvnDByz.mjs} +2 -2
- package/dist/{impl-DXbxO2VJ.mjs.map → impl-DTvnDByz.mjs.map} +1 -1
- package/dist/{impl-Ds-gQfdc.mjs → impl-DUg1sDwg.mjs} +2 -2
- package/dist/{impl-Ds-gQfdc.mjs.map → impl-DUg1sDwg.mjs.map} +1 -1
- package/dist/{impl-B2ZXtnpX.mjs → impl-DldrBJEa.mjs} +2 -2
- package/dist/{impl-B2ZXtnpX.mjs.map → impl-DldrBJEa.mjs.map} +1 -1
- package/dist/{impl-D-a5xGld.mjs → impl-Dmn1CWts.mjs} +3 -3
- package/dist/{impl-D-a5xGld.mjs.map → impl-Dmn1CWts.mjs.map} +1 -1
- package/dist/impl-Dr9Jezjh2.mjs +2 -0
- package/dist/impl-Dr9Jezjh2.mjs.map +1 -0
- package/dist/{impl-DRKPx_I9.mjs → impl-DtTfsHtL.mjs} +2 -2
- package/dist/{impl-DRKPx_I9.mjs.map → impl-DtTfsHtL.mjs.map} +1 -1
- package/dist/{impl-FerHBNJx2.mjs → impl-DzNEfRoS2.mjs} +2 -2
- package/dist/{impl-FerHBNJx2.mjs.map → impl-DzNEfRoS2.mjs.map} +1 -1
- package/dist/{impl-wBcVYHlA.mjs → impl-HnrcGt_T.mjs} +2 -2
- package/dist/{impl-wBcVYHlA.mjs.map → impl-HnrcGt_T.mjs.map} +1 -1
- package/dist/{impl-D1f9mJL_.mjs → impl-QdSegO65.mjs} +2 -2
- package/dist/{impl-D1f9mJL_.mjs.map → impl-QdSegO65.mjs.map} +1 -1
- package/dist/{impl-C_Ifcb00.mjs → impl-S90NUBGm.mjs} +2 -2
- package/dist/{impl-C_Ifcb00.mjs.map → impl-S90NUBGm.mjs.map} +1 -1
- package/dist/{impl-DtpgD_as.mjs → impl-Tr08dgdk.mjs} +2 -2
- package/dist/{impl-DtpgD_as.mjs.map → impl-Tr08dgdk.mjs.map} +1 -1
- package/dist/{impl-Cb42aI60.mjs → impl-WJBuS_0m.mjs} +2 -2
- package/dist/{impl-Cb42aI60.mjs.map → impl-WJBuS_0m.mjs.map} +1 -1
- package/dist/{impl-DiwxYcux.mjs → impl-b4KAEVci.mjs} +2 -2
- package/dist/{impl-DiwxYcux.mjs.map → impl-b4KAEVci.mjs.map} +1 -1
- package/dist/impl-defjX-97.mjs +3 -0
- package/dist/impl-defjX-97.mjs.map +1 -0
- package/dist/{impl-DTadLabu.mjs → impl-fg0UZYw6.mjs} +2 -2
- package/dist/{impl-DTadLabu.mjs.map → impl-fg0UZYw6.mjs.map} +1 -1
- package/dist/{impl-Dr_l_SLd.mjs → impl-hRK54juc.mjs} +2 -2
- package/dist/{impl-Dr_l_SLd.mjs.map → impl-hRK54juc.mjs.map} +1 -1
- package/dist/{impl-DJcNhpbx.mjs → impl-mjDxMEDR.mjs} +2 -2
- package/dist/{impl-DJcNhpbx.mjs.map → impl-mjDxMEDR.mjs.map} +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +2 -2
- package/dist/{markRequestDataSiloIdsCompleted-BYNMCkhy.mjs → markRequestDataSiloIdsCompleted-C6lV_My-.mjs} +2 -2
- package/dist/{markRequestDataSiloIdsCompleted-BYNMCkhy.mjs.map → markRequestDataSiloIdsCompleted-C6lV_My-.mjs.map} +1 -1
- package/dist/{markSilentPrivacyRequests-gKYivzH1.mjs → markSilentPrivacyRequests-D3bFOMLh.mjs} +2 -2
- package/dist/{markSilentPrivacyRequests-gKYivzH1.mjs.map → markSilentPrivacyRequests-D3bFOMLh.mjs.map} +1 -1
- package/dist/{mergeTranscendInputs-D1CvxQu0.mjs → mergeTranscendInputs-B7WjxYMN.mjs} +3 -3
- package/dist/{mergeTranscendInputs-D1CvxQu0.mjs.map → mergeTranscendInputs-B7WjxYMN.mjs.map} +1 -1
- package/dist/{notifyPrivacyRequestsAdditionalTime-DbERhQ1Q.mjs → notifyPrivacyRequestsAdditionalTime-BGUxsWLN.mjs} +2 -2
- package/dist/{notifyPrivacyRequestsAdditionalTime-DbERhQ1Q.mjs.map → notifyPrivacyRequestsAdditionalTime-BGUxsWLN.mjs.map} +1 -1
- package/dist/{policy-bundle-manifest-BZnBMZQq.mjs → policy-bundle-manifest-RgsjPnfI.mjs} +3 -3
- package/dist/{policy-bundle-manifest-BZnBMZQq.mjs.map → policy-bundle-manifest-RgsjPnfI.mjs.map} +1 -1
- package/dist/{policy-project-discovery-C29UxWwt.mjs → policy-project-discovery-B4cFXUlA.mjs} +71 -68
- package/dist/policy-project-discovery-B4cFXUlA.mjs.map +1 -0
- package/dist/policy-scaffold-config-CwlygiTz.mjs +76 -0
- package/dist/policy-scaffold-config-CwlygiTz.mjs.map +1 -0
- package/dist/printResult-Dmwv78e7.mjs +33 -0
- package/dist/printResult-Dmwv78e7.mjs.map +1 -0
- package/dist/{pullAllDatapoints-Bm-Orhse.mjs → pullAllDatapoints-CNqQSSmF.mjs} +3 -3
- package/dist/{pullAllDatapoints-Bm-Orhse.mjs.map → pullAllDatapoints-CNqQSSmF.mjs.map} +1 -1
- package/dist/pullChunkedCustomSiloOutstandingIdentifiers-BfNNWqeI.mjs +2 -0
- package/dist/{pullChunkedCustomSiloOutstandingIdentifiers-DTGQOw--.mjs.map → pullChunkedCustomSiloOutstandingIdentifiers-BfNNWqeI.mjs.map} +1 -1
- package/dist/{pullManualEnrichmentIdentifiersToCsv-DMC5CQtE.mjs → pullManualEnrichmentIdentifiersToCsv-B_lWa6Yg.mjs} +2 -2
- package/dist/{pullManualEnrichmentIdentifiersToCsv-DMC5CQtE.mjs.map → pullManualEnrichmentIdentifiersToCsv-B_lWa6Yg.mjs.map} +1 -1
- package/dist/{pushCronIdentifiersFromCsv-DhS2z8IG.mjs → pushCronIdentifiersFromCsv-CDhlEf6n.mjs} +2 -2
- package/dist/{pushCronIdentifiersFromCsv-DhS2z8IG.mjs.map → pushCronIdentifiersFromCsv-CDhlEf6n.mjs.map} +1 -1
- package/dist/pushManualEnrichmentIdentifiersFromCsv-Bn_028w5.mjs +2 -0
- package/dist/{pushManualEnrichmentIdentifiersFromCsv-BTJGOmdU.mjs.map → pushManualEnrichmentIdentifiersFromCsv-Bn_028w5.mjs.map} +1 -1
- package/dist/{removeUnverifiedRequestIdentifiers-BkAcUZZN.mjs → removeUnverifiedRequestIdentifiers-DcI9ut4q.mjs} +2 -2
- package/dist/{removeUnverifiedRequestIdentifiers-BkAcUZZN.mjs.map → removeUnverifiedRequestIdentifiers-DcI9ut4q.mjs.map} +1 -1
- package/dist/resolveBundleIdByName-BPrMkU0w.mjs +2 -0
- package/dist/resolveBundleIdByName-BPrMkU0w.mjs.map +1 -0
- package/dist/resolvePolicyBundleVersion-BrVz4LKN.mjs +2 -0
- package/dist/resolvePolicyBundleVersion-BrVz4LKN.mjs.map +1 -0
- package/dist/{retryRequestDataSilos-BoPmcB8-.mjs → retryRequestDataSilos-ZdY2PM0S.mjs} +2 -2
- package/dist/{retryRequestDataSilos-BoPmcB8-.mjs.map → retryRequestDataSilos-ZdY2PM0S.mjs.map} +1 -1
- package/dist/{skipPreflightJobs-DkBdYw-r.mjs → skipPreflightJobs-KdcSw8Td.mjs} +2 -2
- package/dist/{skipPreflightJobs-DkBdYw-r.mjs.map → skipPreflightJobs-KdcSw8Td.mjs.map} +1 -1
- package/dist/{skipRequestDataSilos-BRFiokix.mjs → skipRequestDataSilos-Cy2NfP47.mjs} +2 -2
- package/dist/{skipRequestDataSilos-BRFiokix.mjs.map → skipRequestDataSilos-Cy2NfP47.mjs.map} +1 -1
- package/dist/{streamPrivacyRequestsToCsv-Bt-H5J-y.mjs → streamPrivacyRequestsToCsv-ByRBG8Mb.mjs} +2 -2
- package/dist/{streamPrivacyRequestsToCsv-Bt-H5J-y.mjs.map → streamPrivacyRequestsToCsv-ByRBG8Mb.mjs.map} +1 -1
- package/dist/{syncCodePackages-CPkEukDm.mjs → syncCodePackages-DJnrcX5l.mjs} +2 -2
- package/dist/{syncCodePackages-CPkEukDm.mjs.map → syncCodePackages-DJnrcX5l.mjs.map} +1 -1
- package/dist/updateConsentManagerVersionToLatest-KwMaAkwh.mjs +2 -0
- package/dist/{updateConsentManagerVersionToLatest-DWblAwus.mjs.map → updateConsentManagerVersionToLatest-KwMaAkwh.mjs.map} +1 -1
- package/dist/{uploadConsents-D7Vg8ZRW.mjs → uploadConsents-C5LmI-9T.mjs} +2 -2
- package/dist/{uploadConsents-D7Vg8ZRW.mjs.map → uploadConsents-C5LmI-9T.mjs.map} +1 -1
- package/dist/{uploadPrivacyRequestsFromCsv-B_hQwIn6.mjs → uploadPrivacyRequestsFromCsv-BsYnCgbl.mjs} +2 -2
- package/dist/{uploadPrivacyRequestsFromCsv-B_hQwIn6.mjs.map → uploadPrivacyRequestsFromCsv-BsYnCgbl.mjs.map} +1 -1
- package/package.json +4 -4
- package/dist/app-CR3U95JW.mjs.map +0 -1
- package/dist/buildExamples-B6BdW0FD.mjs +0 -2
- package/dist/buildExamples-B6BdW0FD.mjs.map +0 -1
- package/dist/impl-BqTMPbwc2.mjs +0 -2
- package/dist/impl-BqTMPbwc2.mjs.map +0 -1
- package/dist/impl-CAoIp9Nl2.mjs +0 -3
- package/dist/impl-CAoIp9Nl2.mjs.map +0 -1
- package/dist/impl-Cee7Q1Ne.mjs +0 -2
- package/dist/impl-Cee7Q1Ne.mjs.map +0 -1
- package/dist/impl-DQcgEx7V2.mjs +0 -298
- package/dist/impl-DezV3QMo.mjs +0 -3
- package/dist/impl-DezV3QMo.mjs.map +0 -1
- package/dist/impl-btKJ1-VJ.mjs +0 -3
- package/dist/impl-btKJ1-VJ.mjs.map +0 -1
- package/dist/impl-ijoNwhGI.mjs +0 -2
- package/dist/impl-ijoNwhGI.mjs.map +0 -1
- package/dist/policy-project-discovery-C29UxWwt.mjs.map +0 -1
- package/dist/policy-scaffold-config-BUKAyPad.mjs +0 -76
- package/dist/policy-scaffold-config-BUKAyPad.mjs.map +0 -1
- package/dist/printResult-C2K9ODeE.mjs +0 -33
- package/dist/printResult-C2K9ODeE.mjs.map +0 -1
- package/dist/pullChunkedCustomSiloOutstandingIdentifiers-DTGQOw--.mjs +0 -2
- package/dist/pushManualEnrichmentIdentifiersFromCsv-BTJGOmdU.mjs +0 -2
- package/dist/resolveBundleIdByName-Dm_o5_bg.mjs +0 -2
- package/dist/resolveBundleIdByName-Dm_o5_bg.mjs.map +0 -1
- package/dist/resolvePolicyBundleVersion-DRUwe7fA.mjs +0 -2
- package/dist/resolvePolicyBundleVersion-DRUwe7fA.mjs.map +0 -1
- package/dist/updateConsentManagerVersionToLatest-DWblAwus.mjs +0 -2
package/dist/impl-CAoIp9Nl2.mjs
DELETED
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import{r as e}from"./policyEngineCliError-CZrOHhLv.mjs";import{i as t,n,t as r}from"./printResult-C2K9ODeE.mjs";import{t as i}from"./resolveBundleIdByName-Dm_o5_bg.mjs";import{t as a}from"./resolvePolicyBundleVersion-DRUwe7fA.mjs";import{t as o}from"./done-input-validation-LiTaCEzF.mjs";import{t as s}from"./command-output-BJcDRXNr.mjs";import c from"node:path";import l from"colors";import u from"got";function d(e,t){return`${e}-${t}.tar.gz`}function f(e,t){return[`bundleName ${e.bundleName}`,`version ${e.version}`,`output ${t}`,`sha256 ${e.sha256}`,`sizeBytes ${e.sizeBytes}`,`uploadedAt ${e.uploadedAt}`,`activatedAt ${e.activatedAt??`-`}`].join(`
|
|
2
|
-
`)}async function p({"bundle-name":p,version:m,output:h,auth:g,"transcend-url":_,json:v,debug:y=!1}){o(this.process),e(y);let b=t(_,g),x=s(this.logger,v),S=await i(b,p);if(!S)throw Error(`Policy bundle "${p}" was not found for this organization.`);let C;if(m)C=(await a(b,S.id,{version:m})).id;else{if(!S.activeVersionId)throw Error(`Policy bundle "${p}" has no active version.`);C=S.activeVersionId}x.info(l.green(m?`Fetching download URL for bundle "${p}" version "${m}"...`:`Fetching download URL for active version of bundle "${p}"...`));let w=await n(b.get(`v1/policy-engine/policy-bundles/${S.id}/versions/${C}`).json());if(v){r(this.process.stdout,{json:!0,data:w});return}let T=c.resolve(h??d(p,w.version));x.info(l.green(`Downloading policy bundle to ${T}...`));let E;try{E=await u(w.downloadUrl).buffer()}catch(e){throw Error(`Failed to download policy bundle from the presigned URL: ${e instanceof Error?e.message:String(e)}`,{cause:e})}let D=c.dirname(T);this.fs.mkdirSync(D,{recursive:!0}),this.fs.writeFileSync(T,E),r(this.process.stdout,{json:!1,data:w,renderTable:()=>f(w,T)}),x.info(l.green(`Policy bundle downloaded successfully.`))}export{p as download};
|
|
3
|
-
//# sourceMappingURL=impl-CAoIp9Nl2.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"impl-CAoIp9Nl2.mjs","names":[],"sources":["../src/commands/policy/download/impl.ts"],"sourcesContent":["import path from 'node:path';\n\nimport colors from 'colors';\nimport got from 'got';\n\nimport type { LocalContext } from '../../../context.js';\nimport { selectCommandLogger } from '../../../lib/cli/command-output.js';\nimport { doneInputValidation } from '../../../lib/cli/done-input-validation.js';\nimport { EMPTY_CELL } from '../constants.js';\nimport {\n buildPolicyEngineClient,\n policyEngineRequest,\n printResult,\n resolveBundleByName,\n resolvePolicyBundleVersion,\n setPolicyEngineCliDebug,\n} from '../helpers/index.js';\nimport type { GetPolicyBundleVersionResponse } from '../types.js';\n\n/** CLI flags for `transcend policy download`. */\nexport interface DownloadCommandFlags {\n /** Tenant-unique bundle name */\n 'bundle-name': string;\n /**\n * Caller-supplied version label to download.\n * When omitted, downloads the bundle's currently active version.\n */\n version?: string;\n /** Destination file path; defaults to `{bundleName}-{version}.tar.gz` */\n output?: string;\n /** Transcend API key */\n auth: string;\n /** Transcend API URL */\n 'transcend-url': string;\n /** Print metadata + presigned URL as JSON without writing a file */\n json: boolean;\n /** Include technical error details when a command fails */\n debug?: boolean;\n}\n\n/**\n * Builds the default output path for a downloaded policy bundle.\n *\n * @param bundleName - Tenant-unique bundle name\n * @param version - Caller-supplied version label\n * @returns Relative path `{bundleName}-{version}.tar.gz`\n */\nexport function defaultPolicyDownloadOutputPath(bundleName: string, version: string): string {\n return `${bundleName}-${version}.tar.gz`;\n}\n\n/**\n * Formats download metadata for human-readable CLI output.\n *\n * @param body - Version metadata from the Policy Engine API\n * @param outputPath - Destination path written (or that would be written)\n * @returns Summary lines\n */\nfunction formatDownloadSummary(body: GetPolicyBundleVersionResponse, outputPath: string): string {\n return [\n `bundleName ${body.bundleName}`,\n `version ${body.version}`,\n `output ${outputPath}`,\n `sha256 ${body.sha256}`,\n `sizeBytes ${body.sizeBytes}`,\n `uploadedAt ${body.uploadedAt}`,\n `activatedAt ${body.activatedAt ?? EMPTY_CELL}`,\n ].join('\\n');\n}\n\n/**\n * Download a compiled policy bundle version to disk.\n *\n * Resolves `--bundle-name` and an optional `--version` to IDs, fetches a\n * short-lived presigned S3 URL from the monolith, then downloads the `.tar.gz`\n * bytes directly from S3 (the monolith does not proxy the artifact).\n *\n * When `--version` is omitted, downloads the bundle's currently active version.\n * If the bundle has no active version, exits with an error.\n *\n * @param this - CLI context\n * @param flags - Command flags\n */\nexport async function download(\n this: LocalContext,\n {\n 'bundle-name': bundleName,\n version,\n output,\n auth,\n 'transcend-url': transcendUrl,\n json,\n debug = false,\n }: DownloadCommandFlags,\n): Promise<void> {\n doneInputValidation(this.process);\n setPolicyEngineCliDebug(debug);\n\n const client = buildPolicyEngineClient(transcendUrl, auth);\n const commandLogger = selectCommandLogger(this.logger, json);\n\n const bundle = await resolveBundleByName(client, bundleName);\n if (!bundle) {\n throw new Error(`Policy bundle \"${bundleName}\" was not found for this organization.`);\n }\n\n let versionId: string;\n if (version) {\n const resolvedVersion = await resolvePolicyBundleVersion(client, bundle.id, { version });\n versionId = resolvedVersion.id;\n } else {\n if (!bundle.activeVersionId) {\n throw new Error(`Policy bundle \"${bundleName}\" has no active version.`);\n }\n versionId = bundle.activeVersionId;\n }\n\n commandLogger.info(\n colors.green(\n version\n ? `Fetching download URL for bundle \"${bundleName}\" version \"${version}\"...`\n : `Fetching download URL for active version of bundle \"${bundleName}\"...`,\n ),\n );\n\n const body = await policyEngineRequest(\n client\n .get(`v1/policy-engine/policy-bundles/${bundle.id}/versions/${versionId}`)\n .json<GetPolicyBundleVersionResponse>(),\n );\n\n if (json) {\n printResult(this.process.stdout, {\n json: true,\n data: body,\n });\n return;\n }\n\n const outputPath = path.resolve(\n output ?? defaultPolicyDownloadOutputPath(bundleName, body.version),\n );\n\n commandLogger.info(colors.green(`Downloading policy bundle to ${outputPath}...`));\n\n let bundleBytes: Uint8Array;\n try {\n bundleBytes = await got(body.downloadUrl).buffer();\n } catch (err) {\n throw new Error(\n `Failed to download policy bundle from the presigned URL: ${\n err instanceof Error ? err.message : String(err)\n }`,\n { cause: err },\n );\n }\n\n const outputDir = path.dirname(outputPath);\n this.fs.mkdirSync(outputDir, { recursive: true });\n this.fs.writeFileSync(outputPath, bundleBytes);\n\n printResult(this.process.stdout, {\n json: false,\n data: body,\n renderTable: () => formatDownloadSummary(body, outputPath),\n });\n\n commandLogger.info(colors.green('Policy bundle downloaded successfully.'));\n}\n"],"mappings":"oZA+CA,SAAgB,EAAgC,EAAoB,EAAyB,CAC3F,MAAO,GAAG,EAAW,GAAG,EAAQ,SAUlC,SAAS,EAAsB,EAAsC,EAA4B,CAC/F,MAAO,CACL,eAAe,EAAK,aACpB,eAAe,EAAK,UACpB,eAAe,IACf,eAAe,EAAK,SACpB,eAAe,EAAK,YACpB,eAAe,EAAK,aACpB,eAAe,EAAK,aAAA,MACrB,CAAC,KAAK;EAAK,CAgBd,eAAsB,EAEpB,CACE,cAAe,EACf,UACA,SACA,OACA,gBAAiB,EACjB,OACA,QAAQ,IAEK,CACf,EAAoB,KAAK,QAAQ,CACjC,EAAwB,EAAM,CAE9B,IAAM,EAAS,EAAwB,EAAc,EAAK,CACpD,EAAgB,EAAoB,KAAK,OAAQ,EAAK,CAEtD,EAAS,MAAM,EAAoB,EAAQ,EAAW,CAC5D,GAAI,CAAC,EACH,MAAU,MAAM,kBAAkB,EAAW,wCAAwC,CAGvF,IAAI,EACJ,GAAI,EAEF,GAAY,MADkB,EAA2B,EAAQ,EAAO,GAAI,CAAE,UAAS,CAAC,EAC5D,OACvB,CACL,GAAI,CAAC,EAAO,gBACV,MAAU,MAAM,kBAAkB,EAAW,0BAA0B,CAEzE,EAAY,EAAO,gBAGrB,EAAc,KACZ,EAAO,MACL,EACI,qCAAqC,EAAW,aAAa,EAAQ,MACrE,uDAAuD,EAAW,MACvE,CACF,CAED,IAAM,EAAO,MAAM,EACjB,EACG,IAAI,mCAAmC,EAAO,GAAG,YAAY,IAAY,CACzE,MAAsC,CAC1C,CAED,GAAI,EAAM,CACR,EAAY,KAAK,QAAQ,OAAQ,CAC/B,KAAM,GACN,KAAM,EACP,CAAC,CACF,OAGF,IAAM,EAAa,EAAK,QACtB,GAAU,EAAgC,EAAY,EAAK,QAAQ,CACpE,CAED,EAAc,KAAK,EAAO,MAAM,gCAAgC,EAAW,KAAK,CAAC,CAEjF,IAAI,EACJ,GAAI,CACF,EAAc,MAAM,EAAI,EAAK,YAAY,CAAC,QAAQ,OAC3C,EAAK,CACZ,MAAU,MACR,4DACE,aAAe,MAAQ,EAAI,QAAU,OAAO,EAAI,GAElD,CAAE,MAAO,EAAK,CACf,CAGH,IAAM,EAAY,EAAK,QAAQ,EAAW,CAC1C,KAAK,GAAG,UAAU,EAAW,CAAE,UAAW,GAAM,CAAC,CACjD,KAAK,GAAG,cAAc,EAAY,EAAY,CAE9C,EAAY,KAAK,QAAQ,OAAQ,CAC/B,KAAM,GACN,KAAM,EACN,gBAAmB,EAAsB,EAAM,EAAW,CAC3D,CAAC,CAEF,EAAc,KAAK,EAAO,MAAM,yCAAyC,CAAC"}
|
package/dist/impl-Cee7Q1Ne.mjs
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{r as e}from"./policyEngineCliError-CZrOHhLv.mjs";import{i as t,n,t as r}from"./printResult-C2K9ODeE.mjs";import{t as i}from"./renderTable-CZP2roD1.mjs";import{n as a}from"./resolveBundleIdByName-Dm_o5_bg.mjs";import{t as o}from"./done-input-validation-LiTaCEzF.mjs";import{t as s}from"./command-output-BJcDRXNr.mjs";import c from"colors";async function l({"bundle-name":l,auth:u,"transcend-url":d,limit:f,after:p,json:m,debug:h=!1}){o(this.process),e(h);let g=t(d,u),_=s(this.logger,m),v=await a(g,l);if(!v)throw Error(`Policy bundle "${l}" was not found for this organization.`);_.info(c.green(`Listing versions for bundle "${l}"...`));let y={limit:f};p&&(y.after=p);let b=await n(g.get(`v1/policy-engine/policy-bundles/${v}/versions`,{searchParams:y}).json());r(this.process.stdout,{json:m,data:b,renderTable:()=>{if(b.nodes.length===0)return p?`No more versions for bundle "${l}" (end of results).`:`No versions found for bundle "${l}".`;let e=i([`id`,`version`,`createdAt`,`activatedAt`,`deactivatedAt`],b.nodes.map(e=>[e.id,e.version,e.createdAt,e.activatedAt??`-`,e.deactivatedAt??`-`]));return b.pageInfo.endCursor&&(e+=`\n\nafter: ${b.pageInfo.endCursor}`),e}})}export{l as versions};
|
|
2
|
-
//# sourceMappingURL=impl-Cee7Q1Ne.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"impl-Cee7Q1Ne.mjs","names":[],"sources":["../src/commands/policy/versions/impl.ts"],"sourcesContent":["import colors from 'colors';\n\nimport type { LocalContext } from '../../../context.js';\nimport { selectCommandLogger } from '../../../lib/cli/command-output.js';\nimport { doneInputValidation } from '../../../lib/cli/done-input-validation.js';\nimport { EMPTY_CELL } from '../constants.js';\nimport {\n buildPolicyEngineClient,\n policyEngineRequest,\n printResult,\n renderTable,\n resolveBundleIdByName,\n setPolicyEngineCliDebug,\n} from '../helpers/index.js';\nimport type { PolicyBundleVersionListResponse } from '../types.js';\n\n/** CLI flags for `transcend policy versions`. */\nexport interface VersionsCommandFlags {\n /** Tenant-unique bundle name */\n 'bundle-name': string;\n /** Transcend API key */\n auth: string;\n /** Transcend API URL */\n 'transcend-url': string;\n /** Page size */\n limit: number;\n /** Cursor from a previous page's pageInfo.endCursor */\n after?: string;\n /** Print raw JSON response */\n json: boolean;\n /** Include technical error details when a command fails */\n debug?: boolean;\n}\n\n/**\n * List versions for a policy bundle.\n *\n * @param this - CLI context\n * @param flags - Command flags\n */\nexport async function versions(\n this: LocalContext,\n {\n 'bundle-name': bundleName,\n auth,\n 'transcend-url': transcendUrl,\n limit,\n after,\n json,\n debug = false,\n }: VersionsCommandFlags,\n): Promise<void> {\n doneInputValidation(this.process);\n setPolicyEngineCliDebug(debug);\n\n const client = buildPolicyEngineClient(transcendUrl, auth);\n const commandLogger = selectCommandLogger(this.logger, json);\n const bundleId = await resolveBundleIdByName(client, bundleName);\n\n if (!bundleId) {\n throw new Error(`Policy bundle \"${bundleName}\" was not found for this organization.`);\n }\n\n commandLogger.info(colors.green(`Listing versions for bundle \"${bundleName}\"...`));\n\n const searchParams: Record<string, string | number> = { limit };\n if (after) {\n searchParams.after = after;\n }\n\n const body = await policyEngineRequest(\n client\n .get(`v1/policy-engine/policy-bundles/${bundleId}/versions`, {\n searchParams,\n })\n .json<PolicyBundleVersionListResponse>(),\n );\n\n printResult(this.process.stdout, {\n json,\n data: body,\n renderTable: () => {\n if (body.nodes.length === 0) {\n // Distinguish \"this page is empty because you paginated past the end\"\n // from \"the bundle genuinely has no versions\" — the latter is already\n // surfaced during version resolution upstream.\n return after\n ? `No more versions for bundle \"${bundleName}\" (end of results).`\n : `No versions found for bundle \"${bundleName}\".`;\n }\n\n const rows = body.nodes.map((version) => [\n version.id,\n version.version,\n version.createdAt,\n version.activatedAt ?? EMPTY_CELL,\n version.deactivatedAt ?? EMPTY_CELL,\n ]);\n\n let table = renderTable(['id', 'version', 'createdAt', 'activatedAt', 'deactivatedAt'], rows);\n if (body.pageInfo.endCursor) {\n table += `\\n\\nafter: ${body.pageInfo.endCursor}`;\n }\n return table;\n },\n });\n}\n"],"mappings":"yVAwCA,eAAsB,EAEpB,CACE,cAAe,EACf,OACA,gBAAiB,EACjB,QACA,QACA,OACA,QAAQ,IAEK,CACf,EAAoB,KAAK,QAAQ,CACjC,EAAwB,EAAM,CAE9B,IAAM,EAAS,EAAwB,EAAc,EAAK,CACpD,EAAgB,EAAoB,KAAK,OAAQ,EAAK,CACtD,EAAW,MAAM,EAAsB,EAAQ,EAAW,CAEhE,GAAI,CAAC,EACH,MAAU,MAAM,kBAAkB,EAAW,wCAAwC,CAGvF,EAAc,KAAK,EAAO,MAAM,gCAAgC,EAAW,MAAM,CAAC,CAElF,IAAM,EAAgD,CAAE,QAAO,CAC3D,IACF,EAAa,MAAQ,GAGvB,IAAM,EAAO,MAAM,EACjB,EACG,IAAI,mCAAmC,EAAS,WAAY,CAC3D,eACD,CAAC,CACD,MAAuC,CAC3C,CAED,EAAY,KAAK,QAAQ,OAAQ,CAC/B,OACA,KAAM,EACN,gBAAmB,CACjB,GAAI,EAAK,MAAM,SAAW,EAIxB,OAAO,EACH,gCAAgC,EAAW,qBAC3C,iCAAiC,EAAW,IAWlD,IAAI,EAAQ,EAAY,CAAC,KAAM,UAAW,YAAa,cAAe,gBAAgB,CARzE,EAAK,MAAM,IAAK,GAAY,CACvC,EAAQ,GACR,EAAQ,QACR,EAAQ,UACR,EAAQ,aAAA,IACR,EAAQ,eAAA,IACT,CAE2F,CAAC,CAI7F,OAHI,EAAK,SAAS,YAChB,GAAS,cAAc,EAAK,SAAS,aAEhC,GAEV,CAAC"}
|
package/dist/impl-DQcgEx7V2.mjs
DELETED
|
@@ -1,298 +0,0 @@
|
|
|
1
|
-
import{g as e}from"./constants-BRsj50Nb.mjs";import{i as t,n,r,t as i}from"./project-plan-output-BHHlkLxe.mjs";import{c as a,d as o,f as s,l as c,t as l,u}from"./project-discovery-CWRjlFnC.mjs";import{c as d,g as f,l as p,r as m,t as h}from"./policy-project-discovery-C29UxWwt.mjs";import{a as g,c as _,i as v,l as y,o as b,r as ee,s as x,t as S}from"./policy-runtime-6jmkXlBk.mjs";import{t as C}from"./done-input-validation-LiTaCEzF.mjs";import{i as w,n as T,r as E,t as D}from"./prompts-BhXfYD6g.mjs";import{t as O}from"./run-captured-process-BjaSDWXG.mjs";import{t as k}from"./project-plan-apply-Bg_1eMX9.mjs";import{a as A,i as j,n as te,r as ne,t as M}from"./policy-scaffold-config-BUKAyPad.mjs";import{isAbsolute as N,join as P,posix as F,relative as I,sep as L}from"node:path";import R from"colors";const z={Editor:`editor`,Skill:`skill`,Ci:`ci`};var B=`# Idiomatic Rego and document-tree authoring
|
|
2
|
-
|
|
3
|
-
## Start from the document tree
|
|
4
|
-
|
|
5
|
-
OPA evaluates queries against one document tree:
|
|
6
|
-
|
|
7
|
-
- \`input\` is the request-specific document supplied by the caller.
|
|
8
|
-
- \`data\` contains loaded policy packages and static data.
|
|
9
|
-
- A Rego \`package\` determines where a module contributes documents below \`data\`.
|
|
10
|
-
|
|
11
|
-
Choose package paths from the application's required query and ownership
|
|
12
|
-
boundaries. Keep directory and package names aligned
|
|
13
|
-
(\`directory-package-mismatch\`), and cover every publishable package with a
|
|
14
|
-
\`.manifest\` root.
|
|
15
|
-
|
|
16
|
-
### Multi-bundle template layout
|
|
17
|
-
|
|
18
|
-
Prefer a publish directory named \`{root}-bundle/\` so the package-root folder can
|
|
19
|
-
be \`{root}/\` without doubling names:
|
|
20
|
-
|
|
21
|
-
\`\`\`
|
|
22
|
-
example-bundle/ # transcend policy publish … <dir>
|
|
23
|
-
.manifest # roots + optional metadata.transcend.io.template
|
|
24
|
-
example/
|
|
25
|
-
result/… # package example.result
|
|
26
|
-
input.example.json
|
|
27
|
-
input.json # gitignored; VS Code / Regal Evaluate
|
|
28
|
-
\`\`\`
|
|
29
|
-
|
|
30
|
-
Permissions starters use the same layout. Upload does not branch on template
|
|
31
|
-
metadata; Permissions API still loads only the remote bundle named
|
|
32
|
-
\`permissions\` (publish with \`--bundle-name=permissions\`).
|
|
33
|
-
|
|
34
|
-
OPA \`.rego\` placement follows the \`package\` line; \`data.json\` paths follow the
|
|
35
|
-
filesystem and must sit under the manifest root prefix on disk.
|
|
36
|
-
|
|
37
|
-
Use Rego v1 explicitly:
|
|
38
|
-
|
|
39
|
-
\`\`\`rego
|
|
40
|
-
package example.result
|
|
41
|
-
|
|
42
|
-
import rego.v1
|
|
43
|
-
\`\`\`
|
|
44
|
-
|
|
45
|
-
When one publishable package reads another package under the same manifest root,
|
|
46
|
-
import it and use the local name:
|
|
47
|
-
|
|
48
|
-
\`\`\`rego
|
|
49
|
-
import data.example.facts
|
|
50
|
-
|
|
51
|
-
allow if facts.subject_is_trusted
|
|
52
|
-
\`\`\`
|
|
53
|
-
|
|
54
|
-
Do not use a fully qualified \`data.<root>…\` reference directly in a rule body.
|
|
55
|
-
Policy Engine re-namespaces package declarations and imports during activation,
|
|
56
|
-
and \`transcend policy lint\` rejects direct self-root references that would
|
|
57
|
-
change meaning.
|
|
58
|
-
|
|
59
|
-
## Define the contract before implementation
|
|
60
|
-
|
|
61
|
-
Find the actual caller and document:
|
|
62
|
-
|
|
63
|
-
1. the query path below \`data\`;
|
|
64
|
-
2. required and optional \`input\` fields;
|
|
65
|
-
3. the result document consumed by the caller;
|
|
66
|
-
4. fail-closed behavior for absent, malformed, or unknown input;
|
|
67
|
-
5. stable reason codes and any fields reserved for later extension.
|
|
68
|
-
|
|
69
|
-
Do not infer a product contract from the generated example. Replace its package
|
|
70
|
-
names and fields when the repository's contract is known.
|
|
71
|
-
|
|
72
|
-
Wire input JSON Schemas with \`# METADATA\` \`schemas:\` annotations and a schemas
|
|
73
|
-
directory passed to \`opa check -s\` / VS Code \`opa.schema\`.
|
|
74
|
-
|
|
75
|
-
## Prefer declarative result construction
|
|
76
|
-
|
|
77
|
-
String decisions inside an object remain readable and extensible. Keep one
|
|
78
|
-
result document shape and derive its fields from named rules:
|
|
79
|
-
|
|
80
|
-
\`\`\`rego
|
|
81
|
-
default decision := "deny"
|
|
82
|
-
|
|
83
|
-
decision := "allow" if {
|
|
84
|
-
input.subject.trusted == true
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
default reason_code := "untrusted_or_invalid_input"
|
|
88
|
-
|
|
89
|
-
reason_code := "trusted_subject" if {
|
|
90
|
-
decision == "allow"
|
|
91
|
-
}
|
|
92
|
-
\`\`\`
|
|
93
|
-
|
|
94
|
-
Rego rules define documents; they are not an imperative sequence of variable
|
|
95
|
-
assignments. Prefer small named predicates and one result constructor over
|
|
96
|
-
repeated branch-specific copies of the full output object.
|
|
97
|
-
|
|
98
|
-
## Fail closed
|
|
99
|
-
|
|
100
|
-
- Give the externally queried decision or result a deny/error default.
|
|
101
|
-
- Treat missing fields and unknown enum values as non-authorizing.
|
|
102
|
-
- Avoid broad truthiness checks when exact values are required.
|
|
103
|
-
- Add tests for malformed and incomplete inputs, not only expected allow cases.
|
|
104
|
-
- Add result fields compatibly so existing consumers can continue reading the
|
|
105
|
-
stable core.
|
|
106
|
-
`,V=`# Publishing
|
|
107
|
-
|
|
108
|
-
## Validate the upload contract
|
|
109
|
-
|
|
110
|
-
\`.manifest\` declares the document-tree roots included in the Policy Engine
|
|
111
|
-
bundle. Each non-test Rego package must equal a root or be nested below one.
|
|
112
|
-
Test modules remain local and are excluded from the upload archive.
|
|
113
|
-
|
|
114
|
-
In this template, the publish directory is \`{root}-bundle/\` (the folder that
|
|
115
|
-
contains \`.manifest\`), not the inner \`{root}/\` package tree alone.
|
|
116
|
-
|
|
117
|
-
Scaffolded manifests may include optional OPA \`metadata\`:
|
|
118
|
-
|
|
119
|
-
\`\`\`json
|
|
120
|
-
"metadata": {
|
|
121
|
-
"transcend.io": {
|
|
122
|
-
"template": "generic"
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
\`\`\`
|
|
126
|
-
|
|
127
|
-
\`template\` is an **authoring hint** (\`generic\` or \`permissions\`). Upload does
|
|
128
|
-
not branch on it.
|
|
129
|
-
|
|
130
|
-
Before publishing:
|
|
131
|
-
|
|
132
|
-
1. confirm the intended package and query paths with the consuming application;
|
|
133
|
-
2. run \`transcend policy lint <bundle-directory> --noInteractive\` and tests;
|
|
134
|
-
3. review the generated bundle name, version, description, and target Transcend environment;
|
|
135
|
-
4. keep API keys in the environment or the repository's approved secret store.
|
|
136
|
-
|
|
137
|
-
The publish path validates strict Rego compatibility and bundle compilation
|
|
138
|
-
before creating the upload tarball.
|
|
139
|
-
|
|
140
|
-
## Bundle names: generic vs Permissions
|
|
141
|
-
|
|
142
|
-
Upload uses one create/version path for every bundle. What makes a bundle
|
|
143
|
-
“Permissions” is convention plus where Sombra queries it:
|
|
144
|
-
|
|
145
|
-
- **Permissions API** always loads the fixed remote bundle name
|
|
146
|
-
\`permissions\`. Publish that starter with \`--bundle-name=permissions\`.
|
|
147
|
-
- **Generic / decide** bundles use any other \`--bundle-name\` and the decide
|
|
148
|
-
path.
|
|
149
|
-
|
|
150
|
-
\`policy publish\` may warn when \`.manifest\` metadata/roots look Permissions-
|
|
151
|
-
oriented but \`--bundle-name\` is not \`permissions\` (or the reverse). The warning
|
|
152
|
-
does not block upload.
|
|
153
|
-
|
|
154
|
-
## Publish deliberately
|
|
155
|
-
|
|
156
|
-
\`\`\`sh
|
|
157
|
-
# Generic / decide path
|
|
158
|
-
transcend policy publish --bundle-name example --auth "$TRANSCEND_API_KEY" \\
|
|
159
|
-
transcend/policy/example-bundle
|
|
160
|
-
|
|
161
|
-
# Permissions API path (fixed remote name)
|
|
162
|
-
transcend policy publish --bundle-name permissions --auth "$TRANSCEND_API_KEY" \\
|
|
163
|
-
transcend/policy/permissions-bundle
|
|
164
|
-
\`\`\`
|
|
165
|
-
|
|
166
|
-
Run \`transcend policy publish --help\` for current flags and scopes. Publishing
|
|
167
|
-
creates or updates remote Policy Engine bundle resources, so keep it out of
|
|
168
|
-
pull-request validation and require the repository's normal deployment approval.
|
|
169
|
-
|
|
170
|
-
Publish each \`{root}-bundle/\` separately with its own \`--bundle-name\`. Do not
|
|
171
|
-
add publishing, Transcend API credentials, or environment activation to the
|
|
172
|
-
validation-only workflow generated by \`policy init\`.
|
|
173
|
-
`,H="# Setup and tooling\n\n## Install the CLI\n\nInstall the Transcend CLI **≥ 11**:\n\n```sh\nnpm install --global @transcend-io/cli@^11\ntranscend policy --help # should list init, new, lint [<directory>], …\n```\n\n## Initialize safely\n\nRun `transcend policy init --help` before choosing flags. The default target is\n`transcend/policy`.\n\n`policy init` creates an empty multi-bundle workspace — shared Regal config\n(`project.roots: []`) and a README. No bundles or Rego are created.\n\nInteractive initialization offers VS Code setup, this Agent Skill, and\nvalidation-only GitHub Actions. Non-interactive automation enables only\nexplicitly supplied `--editor`, `--skill`, and `--ci` flags:\n\n```sh\ntranscend policy init ./transcend/policy --editor --skill --ci --noInteractive --yes\n```\n\nUse `--dryRun` to inspect the complete transactional plan. Existing policy\nfiles, manifests, Regal configuration, workflows, and customized managed\nartifacts are preserved.\n\n## Add bundles\n\nAfter initializing the workspace, add bundles with `transcend policy new`:\n\n```sh\ntranscend policy new --template generic --name example --yes\ntranscend policy new --template permissions --name permissions --yes\n```\n\nTemplates available:\n\n- `generic` — A fail-closed teaching entrypoint (default root: `example`)\n- `permissions` — A Permission API starter with purpose preferences (default root: `permissions`)\n\n`policy new` creates the `{name}-bundle/` directory, its `.manifest`, Rego tree,\ninput fixtures, and input schema. It also merges the root into Regal config and\nupdates VS Code settings/tasks when `.vscode` is present.\n\n## Required tools\n\n- Use OPA 1.x for Rego v1 parsing, formatting, checks, tests, and bundle compilation.\n- Use Regal for idiomatic Rego linting and editor language-server features.\n- Run `opa version` and `regal version` to confirm the active executables.\n\nThe generated Regal configuration targets OPA 1.18.2 capabilities. Generated CI\ninstalls OPA 1.18.2 and Regal 0.42.0; local tools only need to satisfy the\ncompatible versions reported by `policy init` and `policy lint`. Follow their\nofficial installation links when a compatible tool is missing.\n\n## Editor and CI\n\nRepository-level VS Code settings should include:\n\n- `opa.roots` — one entry per `{root}-bundle/` publish directory\n- `opa.bundleMode: true` — avoid loading fixture JSON as data\n- `opa.schema` — directory of input JSON Schemas (shared when multiple bundles\n need different envelopes)\n- `json.schemas` — optional validation of `input.json` / `input.example.json`\n\nPreserve custom settings and tasks when adapting generated editor files.\n\nGenerated GitHub Actions CI is read-only and credential-free: it installs pinned\ntools and the pinned Transcend CLI, then runs `transcend policy lint` per\npublish directory (matrix when multiple bundles exist). Locally, bare\n`transcend policy lint` verifies every child under the workspace that contains\na `.manifest`:\n\n```sh\ntranscend policy lint --noInteractive --json\ntranscend policy lint transcend/policy/example-bundle --noInteractive --json\n```\n\nAdapt triggers, runner labels, dependency installation, and caching to\nestablished repository conventions without adding publish steps or secrets.\n",U=`# Testing and debugging
|
|
174
|
-
|
|
175
|
-
## Write executable tests
|
|
176
|
-
|
|
177
|
-
Place tests next to the package they exercise and suffix test modules with
|
|
178
|
-
\`_test.rego\`. A separate \`_test\` package makes the production API and test
|
|
179
|
-
helpers easy to distinguish. Keep the \`_test\` package under the same manifest
|
|
180
|
-
root (for example \`example.result_test\`, not a sibling root), so bundle-mode
|
|
181
|
-
loads accept it.
|
|
182
|
-
|
|
183
|
-
\`\`\`rego
|
|
184
|
-
package example.result_test
|
|
185
|
-
|
|
186
|
-
import data.example.result
|
|
187
|
-
import rego.v1
|
|
188
|
-
|
|
189
|
-
test_denies_missing_input if {
|
|
190
|
-
result.decision == "deny" with input as {}
|
|
191
|
-
result.reason_code == "untrusted_or_invalid_input" with input as {}
|
|
192
|
-
}
|
|
193
|
-
\`\`\`
|
|
194
|
-
|
|
195
|
-
Cover at least:
|
|
196
|
-
|
|
197
|
-
- the ordinary allow case;
|
|
198
|
-
- explicit deny cases;
|
|
199
|
-
- absent, malformed, and unknown input;
|
|
200
|
-
- important result fields and reason codes;
|
|
201
|
-
- interactions between packages when one policy reads another document.
|
|
202
|
-
|
|
203
|
-
\`transcend policy lint\` requires a non-empty test suite.
|
|
204
|
-
|
|
205
|
-
## Use local inputs safely
|
|
206
|
-
|
|
207
|
-
Keep a sanitized \`input.example.json\` in each publish directory. \`policy new\`
|
|
208
|
-
also writes a gitignored \`input.json\` beside it (same contents) so VS Code /
|
|
209
|
-
Regal Evaluate work immediately. Customize \`input.json\` locally; keep
|
|
210
|
-
\`input.example.json\` committed and free of credentials or personal data.
|
|
211
|
-
|
|
212
|
-
## Run the complete gate
|
|
213
|
-
|
|
214
|
-
With no directory argument, \`policy lint\` and \`policy test\` verify every
|
|
215
|
-
immediate child under the workspace (\`transcend/policy\`) that contains a
|
|
216
|
-
\`.manifest\`. Pass one bundle path to target a single unit:
|
|
217
|
-
|
|
218
|
-
\`\`\`sh
|
|
219
|
-
transcend policy lint --noInteractive
|
|
220
|
-
transcend policy test
|
|
221
|
-
transcend policy lint transcend/policy/example-bundle --noInteractive
|
|
222
|
-
transcend policy test transcend/policy/example-bundle
|
|
223
|
-
opa check --strict -b transcend/policy/example-bundle -s transcend/policy/schemas
|
|
224
|
-
\`\`\`
|
|
225
|
-
|
|
226
|
-
\`policy lint\` and \`policy test\` run \`opa test -b\` (bundle mode) so a local
|
|
227
|
-
gitignored \`input.json\` beside \`input.example.json\` does not cause a merge
|
|
228
|
-
error. Prefer the CLI over raw directory-mode \`opa test\`. Strict OPA checks use
|
|
229
|
-
Rego v1 (no \`--v0-compatible\`).
|
|
230
|
-
|
|
231
|
-
Use \`transcend policy lint --help\` for formatter flags. Review Regal findings
|
|
232
|
-
deliberately instead of applying broad rewrites.
|
|
233
|
-
|
|
234
|
-
## Evaluate a query
|
|
235
|
-
|
|
236
|
-
\`policy eval\` requires an explicit bundle directory:
|
|
237
|
-
|
|
238
|
-
\`\`\`sh
|
|
239
|
-
transcend policy eval transcend/policy/example-bundle \\
|
|
240
|
-
--package data.example.result \\
|
|
241
|
-
--input transcend/policy/example-bundle/input.json
|
|
242
|
-
\`\`\`
|
|
243
|
-
|
|
244
|
-
Or pipe the envelope with \`--stdin-input\`. Use \`transcend policy eval --help\` for
|
|
245
|
-
curated \`opa eval\` pass-through flags (format, explain, metrics, and similar).
|
|
246
|
-
Exit-on-result flags like OPA \`--fail\` are not exposed — production Evaluate uses
|
|
247
|
-
the Data API (policy deny is a successful evaluation).
|
|
248
|
-
|
|
249
|
-
Or \`opa eval -b … -i …\` with the caller's query path. If the result differs from
|
|
250
|
-
expectations, inspect package paths, imports, defaults, input field presence,
|
|
251
|
-
and the selected bundle directory before changing policy logic.
|
|
252
|
-
`,W=`---
|
|
253
|
-
name: transcend-policy-engine
|
|
254
|
-
description: Sets up, authors, validates, tests, and publishes Transcend Policy Engine projects backed by OPA and Rego. Use for .manifest roots, Rego v1 policy trees, Regal configuration, local policy inputs, Policy Engine CI, or transcend policy CLI commands.
|
|
255
|
-
compatibility: Requires OPA 1.x, Regal, and Transcend CLI ≥ 11 for local validation.
|
|
256
|
-
---
|
|
257
|
-
|
|
258
|
-
# Transcend Policy Engine
|
|
259
|
-
|
|
260
|
-
Use the CLI for deterministic scaffolding and validation. Treat generated
|
|
261
|
-
examples as disposable teaching material and derive the real policy contract
|
|
262
|
-
from repository code and product requirements.
|
|
263
|
-
|
|
264
|
-
Requires **Transcend CLI ≥ 11** (\`transcend policy lint [<directory>]\`, with
|
|
265
|
-
\`init\` available).
|
|
266
|
-
|
|
267
|
-
## Workflow
|
|
268
|
-
|
|
269
|
-
1. Locate each publishable bundle directory (a directory with a \`.manifest\`;
|
|
270
|
-
\`policy new\` scaffolds these as \`{root}-bundle/\` with a \`{root}/\` package
|
|
271
|
-
tree inside).
|
|
272
|
-
2. For initialization, editor support, tools, or CI, read [references/setup-tooling.md](references/setup-tooling.md).
|
|
273
|
-
3. For package layout, document-tree design, decisions, and inputs, read [references/authoring.md](references/authoring.md).
|
|
274
|
-
4. For tests, local inputs, linting, and evaluation, read [references/testing-debugging.md](references/testing-debugging.md).
|
|
275
|
-
5. For bundle validation and publishing, read [references/publishing.md](references/publishing.md).
|
|
276
|
-
6. Preserve repository conventions and finish with:
|
|
277
|
-
|
|
278
|
-
\`\`\`sh
|
|
279
|
-
transcend policy lint --noInteractive
|
|
280
|
-
\`\`\`
|
|
281
|
-
|
|
282
|
-
## Documentation
|
|
283
|
-
|
|
284
|
-
- Every Transcend CLI command and subcommand provides contextual help with \`transcend <command> --help\`.
|
|
285
|
-
- Use the [Transcend CLI README](https://github.com/transcend-io/tools/tree/main/packages/cli#readme) for command documentation and examples.
|
|
286
|
-
- Use the [Transcend documentation index](https://docs.transcend.io/llms.txt) to find current product documentation.
|
|
287
|
-
|
|
288
|
-
## Guardrails
|
|
289
|
-
|
|
290
|
-
- Do not invent an input shape, output contract, package tree, or deployment convention.
|
|
291
|
-
- Keep decisions fail-closed and make result documents extensible.
|
|
292
|
-
- Never commit credentials or a real local \`input.json\`.
|
|
293
|
-
- Keep every publishable Rego package covered by a \`.manifest\` root.
|
|
294
|
-
- One publish directory = one Policy Engine bundle; do not merge unrelated
|
|
295
|
-
products into a single \`.manifest\` unless they share activation and callers.
|
|
296
|
-
`;const G=`transcend-policy-engine`,K=[{path:`SKILL.md`,contents:W},{path:`references/setup-tooling.md`,contents:H},{path:`references/authoring.md`,contents:B},{path:`references/testing-debugging.md`,contents:U},{path:`references/publishing.md`,contents:V}];function q(e){return`${e.hasSkill?`Use the \`${G}\` skill and run`:`Run`} ${e.newCommand} to add a bundle from a template, then adapt the policy document tree and input/output contract to the intended application.`}function J(e,n){return{version:e.version,command:e.command,applied:n.applied,dryRun:n.dryRun,targetDirectory:e.targetDirectory,manifestPath:e.manifestPath,changes:e.changes.map(e=>t(n.cwd,e)),warnings:e.warnings,nextSteps:e.nextSteps,features:[...e.features],aiHandoff:n.aiHandoff,tools:n.tools}}const Y={name:G,displayName:`Policy Engine`,owner:`@transcend-io/cli`,files:K};function X(e,t){let n=new Set(f().map(({path:t})=>P(e.targetDirectory,t))),r=new Set(t.features);return r.has(z.Editor)&&(n.add(P(e.projectRoot,`.vscode`,`settings.json`)),n.add(P(e.projectRoot,`.vscode`,`extensions.json`)),n.add(P(e.projectRoot,`.vscode`,`tasks.json`))),r.has(z.Skill)&&a(e.projectRoot,e.existingSkillDirectories,Y).forEach(e=>n.add(e)),r.has(z.Ci)&&e.repositoryRoot&&e.usesGithub&&n.add(P(e.repositoryRoot,j)),[...n].sort((e,t)=>e.localeCompare(t))}function Z(e,t){let n=I(e,t);if(n===`..`||n.startsWith(`..${L}`)||N(n))throw Error(`Refusing to modify path outside project root: ${t}`)}function re(e){let t=new Set([`${d}/input.json`]);return e.forEach(({path:e})=>{t.add(e);let n=F.dirname(e);for(;n!==`.`;)t.add(n),n=F.dirname(n)}),t}function Q(e,t,n){let r=s({snapshot:u(t.snapshots,n.path),after:n.contents,description:n.description,...n.createOnly===void 0?{}:{createOnly:n.createOnly}});r?e.changes.push(r):e.unchanged.push(n.path)}function ie(e,t,n,r,a,o){Q(e,t,{path:n,contents:r.contents,description:a}),e.warnings.push(...r.warnings.map(e=>`${e} (${i(o,n)})`))}function ae(e,t){let{state:n}=e,r=f(),a=`transcend policy new`,s={version:3,command:`init`,rootDirectory:n.projectRoot,targetDirectory:n.targetDirectory,manifestPath:n.targetDirectory,changes:[],unchanged:[],warnings:[],nextSteps:[a],features:[...t.features]},l=X(n,t);if(Z(n.projectRoot,n.targetDirectory),l.forEach(e=>Z(n.projectRoot,e)),n.relativeFilePaths.length===0)s.directoryPreconditions=[{path:n.targetDirectory,relativePaths:[...n.relativePaths]}],r.forEach(t=>{let r=P(n.targetDirectory,t.path);if(o(e.snapshots,r).kind!==`absent`)throw Error(`Expected an empty policy target, but found an entry at: ${r}`);Q(s,e,{path:r,contents:t.contents,description:t.description,createOnly:!0})});else{let t=!0;r.forEach(r=>{let a=P(n.targetDirectory,r.path),c=o(e.snapshots,a);if(c.kind===`file`&&c.contents===r.contents){s.unchanged.push(a);return}t=!1,c.kind!==`absent`&&(s.unchanged.push(a),s.warnings.push(`Existing policy scaffold path was left unchanged: ${i(n.invocationDirectory,a)}`))});let c=re(r),l=n.relativeFilePaths.some(e=>!c.has(e));(!t||l)&&s.warnings.push(`Existing policy target contains custom or partial content, so no workspace files were added or overwritten. Review ${i(n.invocationDirectory,n.targetDirectory)} manually, then run \`${a}\`.`)}let u=new Set(t.features),d=n.projectRoot;if(u.has(z.Editor)){let t=P(d,`.vscode`,`settings.json`),r=P(d,`.vscode`,`extensions.json`),a=P(d,`.vscode`,`tasks.json`);[{path:t,description:`Merge strict target-scoped OPA and Rego editor settings`,merge:(e,t,n)=>te(e,t,n)},{path:r,description:`Recommend the OPA extension and discourage its syntax-only conflict`,merge:e=>M(e)},{path:a,description:`Create the VS Code task configuration`,merge:(e,t,n)=>ne(e,t,n)}].forEach(({path:t,description:r,merge:a})=>{let c=o(e.snapshots,t);if(c.kind!==`absent`&&c.kind!==`file`){s.unchanged.push(t),s.warnings.push(`Existing VS Code artifact is not a regular file and was left unchanged: ${i(n.invocationDirectory,t)}`);return}ie(s,e,t,a(c.kind===`file`?c.contents:null,d,n.targetDirectory),r,n.invocationDirectory)})}if(u.has(z.Skill)){let t=c({rootDirectory:d,existingDirectories:n.existingSkillDirectories,snapshots:e.snapshots,skill:Y,preserveModified:!0});s.changes.push(...t.changes),s.unchanged.push(...t.unchanged),s.warnings.push(...t.warnings.map(e=>t.unchanged.reduce((e,t)=>e.replace(t,i(n.invocationDirectory,t)),e)))}if(u.has(z.Ci))if(!n.repositoryRoot||!n.usesGithub)s.warnings.push(`GitHub Actions setup was selected, but this target is not inside a detected GitHub repository.`);else{let r=P(n.repositoryRoot,j),a=I(n.repositoryRoot,n.targetDirectory).split(L).join(`/`)||`.`,c=A({cliVersion:t.cliVersion,workspaceDirectory:a,bundleDirectories:[]}),l=o(e.snapshots,r);l.kind===`absent`?Q(s,e,{path:r,contents:c,description:`Add credential-free Policy Engine validation`,createOnly:!0}):l.kind===`file`&&l.contents===c?s.unchanged.push(r):(s.unchanged.push(r),s.warnings.push(`Existing GitHub Actions workflow was left unchanged: ${i(n.invocationDirectory,r)}. Adapt it manually to validate each policy bundle.`))}return s.changes.forEach(({path:e})=>Z(n.projectRoot,e)),s}const oe={[z.Editor]:`VS Code settings and lint task`,[z.Skill]:`Policy Engine Agent Skill`,[z.Ci]:`Validation-only GitHub Actions`},se=Object.values(z);function $(e,t){let n=[t.stdout.trim(),t.stderr.trim()].filter(Boolean).join(`
|
|
297
|
-
`),r=e===`OPA`?ee:g;return`${e===`OPA`?`OPA 1.x`:`Regal ${S} or newer`} is required, but \`${e.toLowerCase()} version\` exited with code ${t.code}${n?`: ${n}`:`.`} Install or upgrade it using the official instructions: ${r}`}async function ce(e,t,n){let r={opa:null,regal:null},i=[],a=await n(`opa`,[`version`],{cwd:t},e);if(a.error?.code===`ENOENT`)i.push(v);else{let e=`${a.stdout}\n${a.stderr}`,t=_(e);a.code===0?t?i.push(t):r.opa=x(e).version:i.push(t??$(`OPA`,a))}let o=await n(`regal`,[`version`],{cwd:t},e);if(o.error?.code===`ENOENT`)i.push(b);else{let e=`${o.stdout}\n${o.stderr}`,t=y(e,S,`the generated OPA ${p} capabilities configuration`);o.code===0?t?i.push(t):r.regal=x(e).version:i.push(t??$(`Regal`,o))}return{tools:r,warnings:i}}async function le(t,a=h,o=O){C(this.process);try{let s=await ce(this,this.process.cwd(),o),c=m(this,a),u=new T(this),d=E(t,this.process.stdin.isTTY,this.process.stderr.isTTY),f=await w(u,{features:se,labels:oe,enabled:{[z.Editor]:t.editor,[z.Skill]:t.skill,[z.Ci]:t.ci},interactive:d}),p=X(c,{features:f}),h=ae({state:c,snapshots:l(this,c.projectRoot,p)},{features:f,cliVersion:e});h.warnings.push(...s.warnings),t.json||this.logger.info(r(h,{cwd:this.process.cwd(),title:`Policy initialization plan`,details:[{label:`Target`,path:h.targetDirectory}]}));let g=h.changes.length===0||t.dryRun;if(h.changes.length>0&&!t.dryRun)if(t.yes)g=!0;else if(d)g=await u.confirm(`Apply this complete plan?`,!0);else throw Error(`The plan requires approval in a non-interactive invocation. Review with --dryRun, then pass --yes.`);g&&!t.dryRun&&h.changes.length>0&&await k(this,h);let _=g&&!t.dryRun&&h.changes.length>0,v=g&&!t.dryRun,y=q({projectPath:n(i(this.process.cwd(),c.targetDirectory)),hasSkill:v&&f.includes(z.Skill),newCommand:h.nextSteps[0]}),b=J(h,{applied:_,dryRun:t.dryRun,cwd:this.process.cwd(),aiHandoff:y,tools:s.tools});if(t.json){this.process.stdout.write(`${JSON.stringify(b)}\n`);return}if(t.dryRun){this.logger.info(R.yellow(`Dry run complete. No changes were written.`));return}if(!g){this.logger.info(R.yellow(`No changes applied.`));return}if(h.changes.length===0){let e=h.warnings.length===s.warnings.length?`Policy project is already initialized.`:`Existing policy project was left unchanged.`;this.logger.info(R.green(e))}else this.logger.info(R.green(`Policy project initialized.`));h.nextSteps.length>0&&(this.logger.info(`\n${R.bold(`Next steps`)}`),h.nextSteps.forEach(e=>this.logger.info(e))),this.logger.info(`\n${R.bold(`AI handoff — paste into your coding agent`)}`),this.logger.info(y)}catch(e){if(e instanceof D)return this.process.exit(130);throw e}}export{le as init};
|
|
298
|
-
//# sourceMappingURL=impl-DQcgEx7V2.mjs.map
|
package/dist/impl-DezV3QMo.mjs
DELETED
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import{r as e,t}from"./policyEngineCliError-CZrOHhLv.mjs";import{i as n,r,t as i}from"./printResult-C2K9ODeE.mjs";import{t as a}from"./formatPolicyBundleVersionSummary-DCHl1Vhy.mjs";import{n as o}from"./resolveBundleIdByName-Dm_o5_bg.mjs";import{t as s}from"./done-input-validation-LiTaCEzF.mjs";import{t as c}from"./command-output-BJcDRXNr.mjs";import l from"colors";async function u({"bundle-name":u,auth:d,"transcend-url":f,json:p,debug:m=!1}){s(this.process),e(m);let h=c(this.logger,p),g=n(f,d),_=await o(g,u);if(!_)throw Error(`Policy bundle "${u}" was not found for this organization.`);h.info(l.green(`Deactivating active version for bundle "${u}"...`));let v;try{v=await g.post(`v1/policy-engine/policy-bundles/${_}/deactivate`).json()}catch(e){if(e?.response?.statusCode===409)throw new t(`Policy bundle "${u}" has no active version.`,{cause:e});r(e)}i(this.process.stdout,{json:p,data:v,renderTable:()=>[`bundleId ${v.bundle.id}`,`bundleName ${v.bundle.bundleName}`,a(v.version)].join(`
|
|
2
|
-
`)}),h.info(l.green(`Policy bundle version deactivated.`))}export{u as deactivate};
|
|
3
|
-
//# sourceMappingURL=impl-DezV3QMo.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"impl-DezV3QMo.mjs","names":[],"sources":["../src/commands/policy/deactivate/impl.ts"],"sourcesContent":["import colors from 'colors';\n\nimport type { LocalContext } from '../../../context.js';\nimport { selectCommandLogger } from '../../../lib/cli/command-output.js';\nimport { doneInputValidation } from '../../../lib/cli/done-input-validation.js';\nimport {\n buildPolicyEngineClient,\n formatPolicyBundleVersionSummary,\n printResult,\n resolveBundleIdByName,\n setPolicyEngineCliDebug,\n throwPolicyEngineRequestError,\n PolicyEngineCliError,\n} from '../helpers/index.js';\nimport type { DeactivatePolicyBundleResponse } from '../types.js';\n\n/** CLI flags for `transcend policy deactivate`. */\nexport interface DeactivateCommandFlags {\n /** Logical bundle name to deactivate */\n 'bundle-name': string;\n /** Transcend API key */\n auth: string;\n /** Transcend API URL */\n 'transcend-url': string;\n /** Print raw JSON response */\n json: boolean;\n /** Include technical error details when a command fails */\n debug?: boolean;\n}\n\n/**\n * Deactivate the currently active version of a policy bundle.\n *\n * Resolves `--bundle-name` to the parent bundle UUID internally, then calls the\n * Policy Engine deactivate endpoint. The bundle is addressed by name only -- the\n * UUID is never exposed to the operator.\n *\n * @param this - CLI context\n * @param flags - Command flags\n */\nexport async function deactivate(\n this: LocalContext,\n {\n 'bundle-name': bundleName,\n auth,\n 'transcend-url': transcendUrl,\n json,\n debug = false,\n }: DeactivateCommandFlags,\n): Promise<void> {\n doneInputValidation(this.process);\n setPolicyEngineCliDebug(debug);\n\n const commandLogger = selectCommandLogger(this.logger, json);\n const client = buildPolicyEngineClient(transcendUrl, auth);\n\n const resolvedBundleId = await resolveBundleIdByName(client, bundleName);\n if (!resolvedBundleId) {\n throw new Error(`Policy bundle \"${bundleName}\" was not found for this organization.`);\n }\n\n commandLogger.info(colors.green(`Deactivating active version for bundle \"${bundleName}\"...`));\n\n let body: DeactivatePolicyBundleResponse;\n try {\n body = await client\n .post(`v1/policy-engine/policy-bundles/${resolvedBundleId}/deactivate`)\n .json<DeactivatePolicyBundleResponse>();\n } catch (err) {\n // The monolith 409 references the internal bundle UUID; rewrite it to the\n // user-supplied bundle name so the error is actionable.\n const statusCode = (err as { response?: { statusCode?: number } })?.response?.statusCode;\n if (statusCode === 409) {\n throw new PolicyEngineCliError(`Policy bundle \"${bundleName}\" has no active version.`, {\n cause: err,\n });\n }\n throwPolicyEngineRequestError(err);\n }\n\n printResult(this.process.stdout, {\n json,\n data: body,\n renderTable: () =>\n [\n `bundleId ${body.bundle.id}`,\n `bundleName ${body.bundle.bundleName}`,\n formatPolicyBundleVersionSummary(body.version),\n ].join('\\n'),\n });\n\n commandLogger.info(colors.green('Policy bundle version deactivated.'));\n}\n"],"mappings":"gXAwCA,eAAsB,EAEpB,CACE,cAAe,EACf,OACA,gBAAiB,EACjB,OACA,QAAQ,IAEK,CACf,EAAoB,KAAK,QAAQ,CACjC,EAAwB,EAAM,CAE9B,IAAM,EAAgB,EAAoB,KAAK,OAAQ,EAAK,CACtD,EAAS,EAAwB,EAAc,EAAK,CAEpD,EAAmB,MAAM,EAAsB,EAAQ,EAAW,CACxE,GAAI,CAAC,EACH,MAAU,MAAM,kBAAkB,EAAW,wCAAwC,CAGvF,EAAc,KAAK,EAAO,MAAM,2CAA2C,EAAW,MAAM,CAAC,CAE7F,IAAI,EACJ,GAAI,CACF,EAAO,MAAM,EACV,KAAK,mCAAmC,EAAiB,aAAa,CACtE,MAAsC,OAClC,EAAK,CAIZ,GADoB,GAAgD,UAAU,aAC3D,IACjB,MAAM,IAAI,EAAqB,kBAAkB,EAAW,0BAA2B,CACrF,MAAO,EACR,CAAC,CAEJ,EAA8B,EAAI,CAGpC,EAAY,KAAK,QAAQ,OAAQ,CAC/B,OACA,KAAM,EACN,gBACE,CACE,eAAe,EAAK,OAAO,KAC3B,eAAe,EAAK,OAAO,aAC3B,EAAiC,EAAK,QAAQ,CAC/C,CAAC,KAAK;EAAK,CACf,CAAC,CAEF,EAAc,KAAK,EAAO,MAAM,qCAAqC,CAAC"}
|
package/dist/impl-btKJ1-VJ.mjs
DELETED
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import{r as e,t}from"./policyEngineCliError-CZrOHhLv.mjs";import{i as n,n as r,t as i}from"./printResult-C2K9ODeE.mjs";import{t as a}from"./formatPolicyBundleVersionSummary-DCHl1Vhy.mjs";import{n as o}from"./resolveBundleIdByName-Dm_o5_bg.mjs";import{t as s}from"./resolvePolicyBundleVersion-DRUwe7fA.mjs";import{t as c}from"./done-input-validation-LiTaCEzF.mjs";import{t as l}from"./command-output-BJcDRXNr.mjs";import u from"colors";async function d({"bundle-name":d,version:f,auth:p,"transcend-url":m,"dry-run":h,json:g,debug:_=!1}){c(this.process),e(_);let v=l(this.logger,g),y=n(m,p),b=await o(y,d);if(!b)throw Error(`Policy bundle "${d}" was not found for this organization.`);let x=await s(y,b,{version:f});v.info(u.green(h?`Validating activation for version "${x.version}"...`:`Activating version "${x.version}"...`));let S;try{S=await r(y.post(`v1/policy-engine/policy-bundles/${b}/versions/${x.id}/activate`,{json:h?{dryRun:!0}:{}}).json())}catch(e){throw(e instanceof t?e.cause:void 0)?.response?.statusCode===409?new t(`Version "${x.version}" of policy bundle "${d}" is already the active version.`,{cause:e}):e}i(this.process.stdout,{json:g,data:S,renderTable:()=>[`bundleId ${S.bundle.id}`,`bundleName ${S.bundle.bundleName}`,a(S.version)].join(`
|
|
2
|
-
`)}),v.info(u.green(h?`Activation validation succeeded.`:`Policy bundle version activated.`))}export{d as activate};
|
|
3
|
-
//# sourceMappingURL=impl-btKJ1-VJ.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"impl-btKJ1-VJ.mjs","names":[],"sources":["../src/commands/policy/activate/impl.ts"],"sourcesContent":["import colors from 'colors';\n\nimport type { LocalContext } from '../../../context.js';\nimport { selectCommandLogger } from '../../../lib/cli/command-output.js';\nimport { doneInputValidation } from '../../../lib/cli/done-input-validation.js';\nimport {\n buildPolicyEngineClient,\n formatPolicyBundleVersionSummary,\n policyEngineRequest,\n PolicyEngineCliError,\n printResult,\n resolveBundleIdByName,\n resolvePolicyBundleVersion,\n setPolicyEngineCliDebug,\n} from '../helpers/index.js';\nimport type { ActivatePolicyBundleVersionResponse } from '../types.js';\n\n/** CLI flags for `transcend policy activate`. */\nexport interface ActivateCommandFlags {\n /** Logical bundle name to activate */\n 'bundle-name': string;\n /** Caller-supplied version label to activate */\n version?: string;\n /** Transcend API key */\n auth: string;\n /** Transcend API URL */\n 'transcend-url': string;\n /** Validate activation without flipping */\n 'dry-run': boolean;\n /** Print raw JSON response */\n json: boolean;\n /** Include technical error details when a command fails */\n debug?: boolean;\n}\n\n/**\n * Activate an uploaded policy bundle version.\n *\n * Resolves `--bundle-name` to the parent bundle UUID internally, then calls the\n * Policy Engine activate endpoint. The bundle is addressed by name only -- the\n * UUID is never exposed to the operator.\n *\n * @param this - CLI context\n * @param flags - Command flags\n */\nexport async function activate(\n this: LocalContext,\n {\n 'bundle-name': bundleName,\n version,\n auth,\n 'transcend-url': transcendUrl,\n 'dry-run': dryRun,\n json,\n debug = false,\n }: ActivateCommandFlags,\n): Promise<void> {\n doneInputValidation(this.process);\n setPolicyEngineCliDebug(debug);\n\n const commandLogger = selectCommandLogger(this.logger, json);\n const client = buildPolicyEngineClient(transcendUrl, auth);\n const resolvedBundleId = await resolveBundleIdByName(client, bundleName);\n if (!resolvedBundleId) {\n throw new Error(`Policy bundle \"${bundleName}\" was not found for this organization.`);\n }\n const resolvedVersion = await resolvePolicyBundleVersion(client, resolvedBundleId, { version });\n\n commandLogger.info(\n colors.green(\n dryRun\n ? `Validating activation for version \"${resolvedVersion.version}\"...`\n : `Activating version \"${resolvedVersion.version}\"...`,\n ),\n );\n\n let body: ActivatePolicyBundleVersionResponse;\n try {\n body = await policyEngineRequest(\n client\n .post(\n `v1/policy-engine/policy-bundles/${resolvedBundleId}/versions/${resolvedVersion.id}/activate`,\n {\n json: dryRun ? { dryRun: true } : {},\n },\n )\n .json<ActivatePolicyBundleVersionResponse>(),\n );\n } catch (err) {\n // A 409 here means the requested version is already the active version --\n // surface that specifically (referencing the operator-facing bundle name\n // and version label) rather than the generic conflict fallback.\n const statusCode = (\n err instanceof PolicyEngineCliError\n ? (err.cause as { response?: { statusCode?: number } } | undefined)\n : undefined\n )?.response?.statusCode;\n if (statusCode === 409) {\n throw new PolicyEngineCliError(\n `Version \"${resolvedVersion.version}\" of policy bundle \"${bundleName}\" is already the active version.`,\n { cause: err },\n );\n }\n throw err;\n }\n\n printResult(this.process.stdout, {\n json,\n data: body,\n renderTable: () =>\n [\n `bundleId ${body.bundle.id}`,\n `bundleName ${body.bundle.bundleName}`,\n formatPolicyBundleVersionSummary(body.version),\n ].join('\\n'),\n });\n\n commandLogger.info(\n colors.green(dryRun ? 'Activation validation succeeded.' : 'Policy bundle version activated.'),\n );\n}\n"],"mappings":"mbA6CA,eAAsB,EAEpB,CACE,cAAe,EACf,UACA,OACA,gBAAiB,EACjB,UAAW,EACX,OACA,QAAQ,IAEK,CACf,EAAoB,KAAK,QAAQ,CACjC,EAAwB,EAAM,CAE9B,IAAM,EAAgB,EAAoB,KAAK,OAAQ,EAAK,CACtD,EAAS,EAAwB,EAAc,EAAK,CACpD,EAAmB,MAAM,EAAsB,EAAQ,EAAW,CACxE,GAAI,CAAC,EACH,MAAU,MAAM,kBAAkB,EAAW,wCAAwC,CAEvF,IAAM,EAAkB,MAAM,EAA2B,EAAQ,EAAkB,CAAE,UAAS,CAAC,CAE/F,EAAc,KACZ,EAAO,MACL,EACI,sCAAsC,EAAgB,QAAQ,MAC9D,uBAAuB,EAAgB,QAAQ,MACpD,CACF,CAED,IAAI,EACJ,GAAI,CACF,EAAO,MAAM,EACX,EACG,KACC,mCAAmC,EAAiB,YAAY,EAAgB,GAAG,WACnF,CACE,KAAM,EAAS,CAAE,OAAQ,GAAM,CAAG,EAAE,CACrC,CACF,CACA,MAA2C,CAC/C,OACM,EAAK,CAeZ,MAVE,aAAe,EACV,EAAI,MACL,IAAA,KACH,UAAU,aACM,IACX,IAAI,EACR,YAAY,EAAgB,QAAQ,sBAAsB,EAAW,kCACrE,CAAE,MAAO,EAAK,CACf,CAEG,EAGR,EAAY,KAAK,QAAQ,OAAQ,CAC/B,OACA,KAAM,EACN,gBACE,CACE,eAAe,EAAK,OAAO,KAC3B,eAAe,EAAK,OAAO,aAC3B,EAAiC,EAAK,QAAQ,CAC/C,CAAC,KAAK;EAAK,CACf,CAAC,CAEF,EAAc,KACZ,EAAO,MAAM,EAAS,mCAAqC,mCAAmC,CAC/F"}
|
package/dist/impl-ijoNwhGI.mjs
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{g as e}from"./constants-BRsj50Nb.mjs";import{n as t,r as n,t as r}from"./project-plan-output-BHHlkLxe.mjs";import{d as i,f as a,t as o,u as s}from"./project-discovery-CWRjlFnC.mjs";import{_ as c,d as l,f as u,h as ee,m as d,o as f,p,r as m,t as h}from"./policy-project-discovery-C29UxWwt.mjs";import{t as g}from"./done-input-validation-LiTaCEzF.mjs";import{n as te,r as _,t as v}from"./prompts-BhXfYD6g.mjs";import{t as y}from"./project-plan-apply-Bg_1eMX9.mjs";import{a as b,i as x,n as S,r as C}from"./policy-scaffold-config-BUKAyPad.mjs";import{join as w,relative as T,sep as E}from"node:path";import D from"colors";const O=/^[a-z][a-z0-9_]*$/u;function k(e){return e?O.test(e)?e.length>64?`Root name cannot exceed 64 characters.`:!0:`Root name must be lowercase alphanumeric with underscores (e.g. "example", "permissions").`:`Enter a bundle root name.`}async function A(O,A=h){g(this.process);try{let h=m(this,A),g=w(h.targetDirectory,`.regal`,`config.yaml`);if(!this.fs.existsSync(g))throw Error(`No policy workspace found at ${r(h.invocationDirectory,h.targetDirectory)}. Run \`transcend policy init\` first.`);let v=new te(this),j=_(O,this.process.stdin.isTTY,this.process.stderr.isTTY),M=O.template??(j?await v.select(`Template:`,u.map(e=>({name:p[e],value:e})),u[0]):void 0);if(!M||!u.includes(M))throw Error(`Missing policy template. Pass --template=${u.join(` or --template=`)}.`);let N=l[M],P=O.name??(j?await v.text(`Bundle root name:`,N,e=>{let t=k(e);return t===!0?!0:t}):void 0);if(!P)throw Error(`Missing bundle root name. Pass --name in a non-interactive invocation.`);let F=k(P);if(F!==!0)throw Error(F);let I=d(P),L=w(h.targetDirectory,I);if(this.fs.existsSync(L))throw Error(`Bundle directory already exists: ${r(h.invocationDirectory,L)}. Choose a different --name.`);let R=ee(M,P),{contents:z,roots:B}=c(this.fs.readFileSync(g,`utf8`),P),V=[g,...R.map(({path:e})=>w(h.targetDirectory,e))],H=w(h.projectRoot,`.vscode`,`settings.json`),U=w(h.projectRoot,`.vscode`,`tasks.json`),W=this.fs.existsSync(w(h.projectRoot,`.vscode`));W&&V.push(H,U);let G=h.repositoryRoot&&h.usesGithub?w(h.repositoryRoot,x):void 0;G&&this.fs.existsSync(G)&&V.push(G);let K=o(this,h.projectRoot,V.sort((e,t)=>e.localeCompare(t))),q=[],J=[],Y=[],X=a({snapshot:s(K,g),after:z,description:`Merge ${P} root into Regal config`});if(X?q.push(X):Y.push(g),R.forEach(e=>{let t=w(h.targetDirectory,e.path);if(i(K,t).kind!==`absent`)throw Error(`Expected an empty path but found an entry at: ${t}`);let n=a({snapshot:s(K,t),after:e.contents,description:e.description});n?q.push(n):Y.push(t)}),W){let e=i(K,H);if(e.kind===`file`||e.kind===`absent`){let t=S(e.kind===`file`?e.contents:null,h.projectRoot,h.targetDirectory,B),n=a({snapshot:e.kind===`file`?{path:H,contents:e.contents,mode:e.mode}:{path:H,contents:null},after:t.contents,description:`Merge bundle root into VS Code OPA settings`});n?q.push(n):Y.push(H),J.push(...t.warnings.map(e=>`${e} (${r(h.invocationDirectory,H)})`))}let t=i(K,U);if(t.kind===`file`||t.kind===`absent`){let e=C(t.kind===`file`?t.contents:null,h.projectRoot,h.targetDirectory,B),n=a({snapshot:t.kind===`file`?{path:U,contents:t.contents,mode:t.mode}:{path:U,contents:null},after:e.contents,description:`Add per-bundle and aggregate lint tasks`});n?q.push(n):Y.push(U),J.push(...e.warnings.map(e=>`${e} (${r(h.invocationDirectory,U)})`))}}if(G&&h.repositoryRoot){let t=i(K,G);if(t.kind===`file`){let n=T(h.repositoryRoot,h.targetDirectory).split(E).join(`/`)||`.`,i=b({cliVersion:e,workspaceDirectory:n,bundleDirectories:B.map(e=>{let t=d(e);return n===`.`?t:`${n}/${t}`})});if(t.contents.startsWith(`# Generated by transcend policy init`)){let e=a({snapshot:{path:G,contents:t.contents,mode:t.mode},after:i,description:`Update Policy Engine CI matrix for all publish directories`});e?q.push(e):Y.push(G)}else J.push(`Existing GitHub Actions workflow was left unchanged: ${r(h.invocationDirectory,G)}. Adapt it manually to lint each \`*-bundle/\` publish directory.`),Y.push(G)}}let Z=`transcend policy lint ${t(r(h.invocationDirectory,L))} --noInteractive`,Q={rootDirectory:h.projectRoot,targetDirectory:h.targetDirectory,manifestPath:w(L,f),changes:q,unchanged:Y,warnings:J,nextSteps:[Z]};O.json||this.logger.info(n(Q,{cwd:this.process.cwd(),title:`Policy bundle plan`,details:[{label:`Workspace`,path:Q.targetDirectory},{label:`Bundle`,path:L}]}));let $=Q.changes.length===0||O.dryRun;if(Q.changes.length>0&&!O.dryRun)if(O.yes)$=!0;else if(j)$=await v.confirm(`Apply this complete plan?`,!0);else throw Error(`The plan requires approval in a non-interactive invocation. Review with --dryRun, then pass --yes.`);$&&!O.dryRun&&Q.changes.length>0&&await y(this,Q);let ne=$&&!O.dryRun&&Q.changes.length>0;if(O.json){this.process.stdout.write(`${JSON.stringify({applied:ne,dryRun:O.dryRun,template:M,root:P,bundleDirectory:L,targetDirectory:Q.targetDirectory,manifestPath:Q.manifestPath,changes:Q.changes.map(e=>({kind:e.kind,target:r(this.process.cwd(),e.path),description:e.description})),warnings:Q.warnings,nextSteps:Q.nextSteps})}\n`);return}if(O.dryRun){this.logger.info(D.yellow(`Dry run complete. No changes were written.`));return}if(!$){this.logger.info(D.yellow(`No changes applied.`));return}this.logger.info(D.green(`Policy bundle ${P} added.`)),Q.nextSteps.length>0&&(this.logger.info(`\n${D.bold(`Next steps`)}`),Q.nextSteps.forEach(e=>this.logger.info(e)))}catch(e){throw e instanceof v&&this.process.exit(130),e}}export{A as _new};
|
|
2
|
-
//# sourceMappingURL=impl-ijoNwhGI.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"impl-ijoNwhGI.mjs","names":["CLI_VERSION"],"sources":["../src/commands/policy/new/impl.ts"],"sourcesContent":["import { join, relative, sep } from 'node:path';\n\nimport colors from 'colors';\n\nimport { version as CLI_VERSION } from '../../../constants.js';\nimport type { LocalContext } from '../../../context.js';\nimport { doneInputValidation } from '../../../lib/cli/done-input-validation.js';\nimport {\n DEFAULT_POLICY_PROJECT_DIRECTORY,\n discoverPolicyProject,\n} from '../../../lib/policy/policy-project-discovery.js';\nimport {\n generatePolicyGithubActionsWorkflow,\n POLICY_CI_WORKFLOW_PATH,\n} from '../../../lib/policy/policy-scaffold-artifacts.js';\nimport {\n mergePolicyEditorSettings,\n mergePolicyEditorTasks,\n} from '../../../lib/policy/policy-scaffold-config.js';\nimport {\n buildBundleDirectoryName,\n generatePolicyBundleFiles,\n mergePolicyRegalConfigRoots,\n POLICY_MANIFEST_FILENAME,\n POLICY_TEMPLATE_DEFAULT_ROOTS,\n POLICY_TEMPLATE_NAMES,\n POLICY_TEMPLATE_PROMPT_LABELS,\n type PolicyTemplateName,\n} from '../../../lib/policy/policy-scaffold-templates.js';\nimport { collectPlanningSnapshots } from '../../../lib/scaffolding/project-discovery.js';\nimport { applyProjectPlan } from '../../../lib/scaffolding/project-plan-apply.js';\nimport {\n displayProjectPath,\n quoteShellArgument,\n renderProjectPlan,\n} from '../../../lib/scaffolding/project-plan-output.js';\nimport {\n planFileChange,\n getPlanningFileSnapshot,\n getPlanningPathSnapshot,\n type PlannedChange,\n type ProjectPlan,\n} from '../../../lib/scaffolding/project-plan.js';\nimport {\n isInteractivePromptInvocation,\n PromptCancelledError,\n ScaffoldPrompts,\n} from '../../../lib/scaffolding/prompts.js';\n\n/** Flags for `transcend policy new`. */\nexport interface PolicyNewFlags {\n /** Package root name. */\n name?: string;\n /** Starter template. */\n template?: PolicyTemplateName;\n /** Disable prompts. */\n noInteractive: boolean;\n /** Render but do not apply. */\n dryRun: boolean;\n /** Approve the final displayed plan. */\n yes: boolean;\n /** Emit one stable JSON result on stdout. */\n json: boolean;\n}\n\n/** Validate a bundle root name. */\nconst VALID_ROOT_PATTERN = /^[a-z][a-z0-9_]*$/u;\n\n/**\n * Validate a policy bundle root name.\n *\n * @param value - Proposed root name\n * @returns True or error message\n */\nfunction validateRootName(value: string): true | string {\n if (!value) {\n return 'Enter a bundle root name.';\n }\n if (!VALID_ROOT_PATTERN.test(value)) {\n return 'Root name must be lowercase alphanumeric with underscores (e.g. \"example\", \"permissions\").';\n }\n if (value.length > 64) {\n return 'Root name cannot exceed 64 characters.';\n }\n return true;\n}\n\n/**\n * Scaffold one policy bundle from a template.\n *\n * @param this - CLI context\n * @param flags - Template and interaction flags\n * @param directory - Target policy workspace directory\n */\nexport async function _new(\n this: LocalContext,\n flags: PolicyNewFlags,\n directory: string = DEFAULT_POLICY_PROJECT_DIRECTORY,\n): Promise<void> {\n doneInputValidation(this.process);\n try {\n const state = discoverPolicyProject(this, directory);\n const regalConfigPath = join(state.targetDirectory, '.regal', 'config.yaml');\n\n if (!this.fs.existsSync(regalConfigPath)) {\n throw new Error(\n `No policy workspace found at ${displayProjectPath(\n state.invocationDirectory,\n state.targetDirectory,\n )}. Run \\`transcend policy init\\` first.`,\n );\n }\n\n const prompts = new ScaffoldPrompts(this);\n const interactive = isInteractivePromptInvocation(\n flags,\n this.process.stdin.isTTY,\n this.process.stderr.isTTY,\n );\n\n const template =\n flags.template ??\n (interactive\n ? await prompts.select<PolicyTemplateName>(\n 'Template:',\n POLICY_TEMPLATE_NAMES.map((value) => ({\n name: POLICY_TEMPLATE_PROMPT_LABELS[value],\n value,\n })),\n POLICY_TEMPLATE_NAMES[0],\n )\n : undefined);\n if (!template || !POLICY_TEMPLATE_NAMES.includes(template)) {\n throw new Error(\n `Missing policy template. Pass --template=${POLICY_TEMPLATE_NAMES.join(' or --template=')}.`,\n );\n }\n\n const defaultRoot = POLICY_TEMPLATE_DEFAULT_ROOTS[template];\n const name =\n flags.name ??\n (interactive\n ? await prompts.text('Bundle root name:', defaultRoot, (value) => {\n const result = validateRootName(value);\n return result === true ? true : result;\n })\n : undefined);\n if (!name) {\n throw new Error('Missing bundle root name. Pass --name in a non-interactive invocation.');\n }\n const rootValidation = validateRootName(name);\n if (rootValidation !== true) {\n throw new Error(rootValidation);\n }\n\n const bundleDir = buildBundleDirectoryName(name);\n const bundlePath = join(state.targetDirectory, bundleDir);\n if (this.fs.existsSync(bundlePath)) {\n throw new Error(\n `Bundle directory already exists: ${displayProjectPath(\n state.invocationDirectory,\n bundlePath,\n )}. Choose a different --name.`,\n );\n }\n\n const files = generatePolicyBundleFiles(template, name);\n const existingRegalContents = this.fs.readFileSync(regalConfigPath, 'utf8');\n const { contents: updatedRegalConfig, roots: allRoots } = mergePolicyRegalConfigRoots(\n existingRegalContents,\n name,\n );\n\n const candidatePaths = [\n regalConfigPath,\n ...files.map(({ path }) => join(state.targetDirectory, path)),\n ];\n\n const vscodeSettingsPath = join(state.projectRoot, '.vscode', 'settings.json');\n const vscodeTasksPath = join(state.projectRoot, '.vscode', 'tasks.json');\n const hasVscode = this.fs.existsSync(join(state.projectRoot, '.vscode'));\n if (hasVscode) {\n candidatePaths.push(vscodeSettingsPath, vscodeTasksPath);\n }\n\n const workflowPath =\n state.repositoryRoot && state.usesGithub\n ? join(state.repositoryRoot, POLICY_CI_WORKFLOW_PATH)\n : undefined;\n if (workflowPath && this.fs.existsSync(workflowPath)) {\n candidatePaths.push(workflowPath);\n }\n\n const snapshots = collectPlanningSnapshots(\n this,\n state.projectRoot,\n candidatePaths.sort((a, b) => a.localeCompare(b)),\n );\n\n const changes: PlannedChange[] = [];\n const warnings: string[] = [];\n const unchanged: string[] = [];\n\n const regalChange = planFileChange({\n snapshot: getPlanningFileSnapshot(snapshots, regalConfigPath),\n after: updatedRegalConfig,\n description: `Merge ${name} root into Regal config`,\n });\n if (regalChange) {\n changes.push(regalChange);\n } else {\n unchanged.push(regalConfigPath);\n }\n\n files.forEach((file) => {\n const path = join(state.targetDirectory, file.path);\n const snapshot = getPlanningPathSnapshot(snapshots, path);\n if (snapshot.kind !== 'absent') {\n throw new Error(`Expected an empty path but found an entry at: ${path}`);\n }\n const change = planFileChange({\n snapshot: getPlanningFileSnapshot(snapshots, path),\n after: file.contents,\n description: file.description,\n });\n if (change) {\n changes.push(change);\n } else {\n unchanged.push(path);\n }\n });\n\n if (hasVscode) {\n const settingsSnapshot = getPlanningPathSnapshot(snapshots, vscodeSettingsPath);\n if (settingsSnapshot.kind === 'file' || settingsSnapshot.kind === 'absent') {\n const settingsResult = mergePolicyEditorSettings(\n settingsSnapshot.kind === 'file' ? settingsSnapshot.contents : null,\n state.projectRoot,\n state.targetDirectory,\n allRoots,\n );\n const settingsChange = planFileChange({\n snapshot:\n settingsSnapshot.kind === 'file'\n ? {\n path: vscodeSettingsPath,\n contents: settingsSnapshot.contents,\n mode: settingsSnapshot.mode,\n }\n : { path: vscodeSettingsPath, contents: null },\n after: settingsResult.contents,\n description: 'Merge bundle root into VS Code OPA settings',\n });\n if (settingsChange) {\n changes.push(settingsChange);\n } else {\n unchanged.push(vscodeSettingsPath);\n }\n warnings.push(\n ...settingsResult.warnings.map(\n (w) => `${w} (${displayProjectPath(state.invocationDirectory, vscodeSettingsPath)})`,\n ),\n );\n }\n\n const tasksSnapshot = getPlanningPathSnapshot(snapshots, vscodeTasksPath);\n if (tasksSnapshot.kind === 'file' || tasksSnapshot.kind === 'absent') {\n const tasksResult = mergePolicyEditorTasks(\n tasksSnapshot.kind === 'file' ? tasksSnapshot.contents : null,\n state.projectRoot,\n state.targetDirectory,\n allRoots,\n );\n const tasksChange = planFileChange({\n snapshot:\n tasksSnapshot.kind === 'file'\n ? {\n path: vscodeTasksPath,\n contents: tasksSnapshot.contents,\n mode: tasksSnapshot.mode,\n }\n : { path: vscodeTasksPath, contents: null },\n after: tasksResult.contents,\n description: 'Add per-bundle and aggregate lint tasks',\n });\n if (tasksChange) {\n changes.push(tasksChange);\n } else {\n unchanged.push(vscodeTasksPath);\n }\n warnings.push(\n ...tasksResult.warnings.map(\n (w) => `${w} (${displayProjectPath(state.invocationDirectory, vscodeTasksPath)})`,\n ),\n );\n }\n }\n\n if (workflowPath && state.repositoryRoot) {\n const workflowSnapshot = getPlanningPathSnapshot(snapshots, workflowPath);\n if (workflowSnapshot.kind === 'file') {\n const workspaceDirectory =\n relative(state.repositoryRoot, state.targetDirectory).split(sep).join('/') || '.';\n const bundleDirectories = allRoots.map((root) => {\n const bundle = buildBundleDirectoryName(root);\n return workspaceDirectory === '.' ? bundle : `${workspaceDirectory}/${bundle}`;\n });\n const desiredWorkflow = generatePolicyGithubActionsWorkflow({\n cliVersion: CLI_VERSION,\n workspaceDirectory,\n bundleDirectories,\n });\n if (workflowSnapshot.contents.startsWith('# Generated by transcend policy init')) {\n const workflowChange = planFileChange({\n snapshot: {\n path: workflowPath,\n contents: workflowSnapshot.contents,\n mode: workflowSnapshot.mode,\n },\n after: desiredWorkflow,\n description: 'Update Policy Engine CI matrix for all publish directories',\n });\n if (workflowChange) {\n changes.push(workflowChange);\n } else {\n unchanged.push(workflowPath);\n }\n } else {\n warnings.push(\n `Existing GitHub Actions workflow was left unchanged: ${displayProjectPath(\n state.invocationDirectory,\n workflowPath,\n )}. Adapt it manually to lint each \\`*-bundle/\\` publish directory.`,\n );\n unchanged.push(workflowPath);\n }\n }\n }\n\n const lintCommand = `transcend policy lint ${quoteShellArgument(\n displayProjectPath(state.invocationDirectory, bundlePath),\n )} --noInteractive`;\n\n const plan: ProjectPlan & {\n /** Absolute workspace directory. */\n targetDirectory: string;\n /** Absolute manifest path. */\n manifestPath: string;\n /** Unchanged paths. */\n unchanged: string[];\n /** Non-fatal warnings. */\n warnings: string[];\n /** Follow-up commands. */\n nextSteps: string[];\n } = {\n rootDirectory: state.projectRoot,\n targetDirectory: state.targetDirectory,\n manifestPath: join(bundlePath, POLICY_MANIFEST_FILENAME),\n changes,\n unchanged,\n warnings,\n nextSteps: [lintCommand],\n };\n\n if (!flags.json) {\n this.logger.info(\n renderProjectPlan(plan, {\n cwd: this.process.cwd(),\n title: 'Policy bundle plan',\n details: [\n { label: 'Workspace', path: plan.targetDirectory },\n { label: 'Bundle', path: bundlePath },\n ],\n }),\n );\n }\n\n let approved = plan.changes.length === 0 || flags.dryRun;\n if (plan.changes.length > 0 && !flags.dryRun) {\n if (flags.yes) {\n approved = true;\n } else if (interactive) {\n approved = await prompts.confirm('Apply this complete plan?', true);\n } else {\n throw new Error(\n 'The plan requires approval in a non-interactive invocation. Review with --dryRun, then pass --yes.',\n );\n }\n }\n\n if (approved && !flags.dryRun && plan.changes.length > 0) {\n await applyProjectPlan(this, plan);\n }\n const applied = approved && !flags.dryRun && plan.changes.length > 0;\n\n if (flags.json) {\n this.process.stdout.write(\n `${JSON.stringify({\n applied,\n dryRun: flags.dryRun,\n template,\n root: name,\n bundleDirectory: bundlePath,\n targetDirectory: plan.targetDirectory,\n manifestPath: plan.manifestPath,\n changes: plan.changes.map((change) => ({\n kind: change.kind,\n target: displayProjectPath(this.process.cwd(), change.path),\n description: change.description,\n })),\n warnings: plan.warnings,\n nextSteps: plan.nextSteps,\n })}\\n`,\n );\n return;\n }\n if (flags.dryRun) {\n this.logger.info(colors.yellow('Dry run complete. No changes were written.'));\n return;\n }\n if (!approved) {\n this.logger.info(colors.yellow('No changes applied.'));\n return;\n }\n this.logger.info(colors.green(`Policy bundle ${name} added.`));\n if (plan.nextSteps.length > 0) {\n this.logger.info(`\\n${colors.bold('Next steps')}`);\n plan.nextSteps.forEach((step) => this.logger.info(step));\n }\n } catch (error) {\n if (error instanceof PromptCancelledError) {\n this.process.exit(130);\n }\n throw error;\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"policy-project-discovery-C29UxWwt.mjs","names":["permissionsPolicyInputSchema.$id","permissionsPolicyInputSchema","permissionsPolicyInputSchema.examples"],"sources":["../schema/permissions-policy-input.json","../src/lib/policy/policy-scaffold-templates.ts","../src/lib/policy/policy-project-discovery.ts"],"sourcesContent":["","import yaml from 'js-yaml';\n\nimport permissionsPolicyInputSchema from '../../../schema/permissions-policy-input.json' with { type: 'json' };\n\n/** Published Permissions API OPA input schema `$id` (raw GitHub URL). */\nexport const PERMISSIONS_POLICY_INPUT_SCHEMA_ID = permissionsPolicyInputSchema.$id;\n\n/** OPA version targeted by generated policy authoring configuration. */\nexport const POLICY_STARTER_OPA_VERSION = '1.18.2';\n\n/**\n * Package-path prefix for the disposable starter bundle.\n *\n * Matches OPA `.manifest` `roots` and Regal `project.roots` (not a filesystem\n * folder name by itself). The publish directory is `{root}-bundle/`.\n */\nexport const POLICY_STARTER_ROOT = 'example';\n\n/** Publish directory name for the disposable starter (`{root}-bundle`). */\nexport const POLICY_STARTER_BUNDLE_DIRECTORY = `${POLICY_STARTER_ROOT}-bundle`;\n\n/** One deterministic file in the safe policy starter. */\nexport interface PolicyStarterFile {\n /** POSIX-style path relative to the policy workspace directory. */\n path: string;\n /** Complete UTF-8 file contents. */\n contents: string;\n /** Human-readable reason shown in the plan. */\n description: string;\n}\n\n/** OPA bundle manifest filename used for local authoring and upload. */\nexport const POLICY_MANIFEST_FILENAME = '.manifest';\n\n/**\n * Remote bundle name reserved for Permissions API.\n *\n * Upload treats every bundle the same; Sombra's Permissions API always queries\n * this fixed name (never taken from the request body).\n */\nexport const PERMISSIONS_POLICY_BUNDLE_NAME = 'permissions';\n\n/** OPA `.manifest` `metadata` key for Transcend authoring hints. */\nexport const POLICY_MANIFEST_TRANSCEND_METADATA_KEY = 'transcend.io';\n\n/** Supported policy bundle template names. */\nexport const POLICY_TEMPLATE_NAMES = ['generic', 'permissions'] as const;\n\n/** A supported policy bundle template name. */\nexport type PolicyTemplateName = (typeof POLICY_TEMPLATE_NAMES)[number];\n\n/**\n * Build an OPA bundle manifest for a given root and scaffold template.\n *\n * `metadata.transcend.io.template` is an authoring hint only — Policy Engine\n * upload does not branch on it. Permissions API still keys off the remote\n * bundle name {@link PERMISSIONS_POLICY_BUNDLE_NAME}.\n *\n * @param root - Package root\n * @param template - Scaffold template that produced this bundle\n * @returns Manifest JSON\n */\nexport function buildPolicyManifestTemplate(\n root: string,\n template: PolicyTemplateName = 'generic',\n): string {\n return `{\n \"$schema\": \"https://openpolicyagent.org/schemas/bundle/v1/manifest.schema.json\",\n \"revision\": \"\",\n \"roots\": [\"${root}\"],\n \"rego_version\": 1,\n \"metadata\": {\n \"${POLICY_MANIFEST_TRANSCEND_METADATA_KEY}\": {\n \"template\": \"${template}\"\n }\n }\n}\n`;\n}\n\n/** OPA bundle manifest for the disposable starter publish directory. */\nexport const POLICY_MANIFEST_TEMPLATE = buildPolicyManifestTemplate(POLICY_STARTER_ROOT, 'generic');\n\n/**\n * Build workspace-level Regal configuration for given roots.\n *\n * `project.roots` are package-path prefixes (same meaning as OPA `.manifest`\n * roots), not filesystem folders.\n *\n * @param roots - Package-path roots\n * @returns YAML configuration\n */\nexport function buildPolicyRegalConfigTemplate(roots: readonly string[]): string {\n const rootsSection =\n roots.length === 0\n ? ' roots: []'\n : ` roots:\\n${roots.map((root) => ` - ${root}`).join('\\n')}`;\n return `# Workspace-level Regal config for both publishable bundles under this tree.\n# \\`project.roots\\` are package-path prefixes (same meaning as OPA \\`.manifest\\`\n# roots), not filesystem folders. Each bundle also has its own \\`.manifest\\`.\ncapabilities:\n from:\n engine: opa\n version: v${POLICY_STARTER_OPA_VERSION}\nproject:\n${rootsSection}\n rego-version: 1\n`;\n}\n\n/**\n * Merge one package-path root into an existing Regal config without wiping\n * unrelated keys (custom rules, ignores, etc.).\n *\n * Comments and key order may be reformatted by the YAML serializer. Invalid\n * `project.roots` shapes throw instead of silently dropping prior roots.\n *\n * @param existingContents - Current `.regal/config.yaml` contents\n * @param rootToAdd - Package-path root to include\n * @returns Updated file contents and the full roots list\n */\nexport function mergePolicyRegalConfigRoots(\n existingContents: string,\n rootToAdd: string,\n): { contents: string; roots: string[] } {\n const trimmed = existingContents.trim();\n if (trimmed.length === 0) {\n const roots = [rootToAdd];\n return { contents: buildPolicyRegalConfigTemplate(roots), roots };\n }\n\n let parsed: unknown;\n try {\n parsed = yaml.load(existingContents);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n throw new Error(`.regal/config.yaml is not valid YAML: ${detail}`);\n }\n\n if (parsed === null || parsed === undefined) {\n const roots = [rootToAdd];\n return { contents: buildPolicyRegalConfigTemplate(roots), roots };\n }\n if (typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new Error('.regal/config.yaml must be a YAML mapping at the top level.');\n }\n\n const config = { ...(parsed as Record<string, unknown>) };\n const existingProject = config.project;\n if (\n existingProject !== undefined &&\n existingProject !== null &&\n (typeof existingProject !== 'object' || Array.isArray(existingProject))\n ) {\n throw new Error('.regal/config.yaml project must be a YAML mapping when present.');\n }\n\n const project: Record<string, unknown> = {\n ...((existingProject as Record<string, unknown> | undefined) ?? {}),\n };\n const existingRoots = project.roots;\n let roots: string[];\n if (existingRoots === undefined) {\n roots = [rootToAdd];\n } else if (\n Array.isArray(existingRoots) &&\n existingRoots.every((root): root is string => typeof root === 'string')\n ) {\n roots = [...new Set([...existingRoots, rootToAdd])].sort((left, right) =>\n left.localeCompare(right),\n );\n } else {\n throw new Error(\n '.regal/config.yaml project.roots must be an array of strings. ' +\n 'Fix the Regal config before running `transcend policy new`.',\n );\n }\n\n project.roots = roots;\n if (project['rego-version'] === undefined) {\n project['rego-version'] = 1;\n }\n config.project = project;\n\n if (config.capabilities === undefined) {\n config.capabilities = {\n from: {\n engine: 'opa',\n version: `v${POLICY_STARTER_OPA_VERSION}`,\n },\n };\n }\n\n const dumped = yaml.dump(config, {\n lineWidth: -1,\n noRefs: true,\n sortKeys: false,\n });\n return {\n contents: dumped.endsWith('\\n') ? dumped : `${dumped}\\n`,\n roots,\n };\n}\n\n/**\n * Workspace-level Regal configuration.\n *\n * `project.roots` are package-path prefixes (same meaning as OPA `.manifest`\n * roots), not filesystem folders.\n */\nexport const POLICY_REGAL_CONFIG_TEMPLATE = buildPolicyRegalConfigTemplate([POLICY_STARTER_ROOT]);\n\n/** Regal config for an empty workspace with no bundles yet. */\nexport const POLICY_REGAL_CONFIG_EMPTY_TEMPLATE = buildPolicyRegalConfigTemplate([]);\n\n/** Disposable, fail-closed example policy. */\nexport const POLICY_RESULT_REGO_TEMPLATE = `# METADATA\n# title: Disposable example result\n# description: |\n# A fail-closed teaching entrypoint. Replace this example with the document\n# tree and result contract required by your application.\n# scope: package\n# schemas:\n# - input: schema.${POLICY_STARTER_ROOT}.input\n# entrypoint: true\npackage ${POLICY_STARTER_ROOT}.result\n\nimport rego.v1\n\ndefault decision := \"deny\"\n\ndecision := \"allow\" if {\n\tinput.subject.trusted == true\n}\n\ndefault reason_code := \"untrusted_or_invalid_input\"\n\nreason_code := \"trusted_subject\" if {\n\tdecision == \"allow\"\n}\n`;\n\n/** Executable tests adjacent to the disposable example policy. */\nexport const POLICY_RESULT_TEST_REGO_TEMPLATE = `package ${POLICY_STARTER_ROOT}.result_test\n\nimport data.${POLICY_STARTER_ROOT}.result\nimport rego.v1\n\ntest_denies_untrusted_subject if {\n\tresult.decision == \"deny\" with input as {\"subject\": {\"trusted\": false}}\n\tresult.reason_code == \"untrusted_or_invalid_input\" with input as {\"subject\": {\"trusted\": false}}\n}\n\ntest_fails_closed_for_missing_subject if {\n\tresult.decision == \"deny\" with input as {}\n\tresult.reason_code == \"untrusted_or_invalid_input\" with input as {}\n}\n\ntest_allows_trusted_subject if {\n\tresult.decision == \"allow\" with input as {\"subject\": {\"trusted\": true}}\n\tresult.reason_code == \"trusted_subject\" with input as {\"subject\": {\"trusted\": true}}\n}\n`;\n\n/** Sanitized local input example containing no credentials or personal data. */\nexport const POLICY_INPUT_EXAMPLE_TEMPLATE = `{\n \"subject\": {\n \"trusted\": false\n }\n}\n`;\n\n/** Input JSON Schema for the disposable starter (editor + \\`opa check -s\\`). */\nexport const POLICY_INPUT_SCHEMA_TEMPLATE = `{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": \"https://transcend.io/policy-schemas/${POLICY_STARTER_ROOT}/input.json\",\n \"title\": \"Example policy input\",\n \"description\": \"Disposable teaching envelope for the example bundle.\",\n \"type\": \"object\",\n \"additionalProperties\": true,\n \"properties\": {\n \"subject\": {\n \"type\": \"object\",\n \"additionalProperties\": true,\n \"properties\": {\n \"trusted\": {\n \"type\": \"boolean\",\n \"description\": \"When true, the example policy allows; otherwise it denies.\"\n }\n }\n }\n }\n}\n`;\n\n/** Target-local ignore rules for private evaluation input only. */\nexport const POLICY_GITIGNORE_TEMPLATE = `# Local policy evaluation input (VS Code / Regal look for input.json here).\n/input.json\n`;\n\n/** Concise workspace-local authoring guide (multi-bundle, empty workspace). */\nexport const POLICY_README_TEMPLATE = `# Transcend Policy\n\nMulti-bundle workspace for [Transcend Policy Engine](https://docs.transcend.io) bundles\n(OPA / Rego v1). Requires **Transcend CLI ≥ 11** (\\`transcend policy --help\\`\nshould list \\`init\\`, \\`new\\`, and positional \\`[directory]\\` args).\n\nEach \\`*-bundle/\\` directory is a separate publishable unit (own \\`.manifest\\`,\ninput, and query path). Add a new bundle with:\n\n\\`\\`\\`sh\ntranscend policy new\n\\`\\`\\`\n\n## Quick start\n\n\\`\\`\\`sh\ntranscend policy init # create the workspace\ntranscend policy new --template generic # add a bundle from a template\n\ntranscend policy lint --noInteractive\ntranscend policy test\ntranscend policy eval transcend/policy/example-bundle \\\\\n --package data.example.result \\\\\n --input transcend/policy/example-bundle/input.json\n\\`\\`\\`\n\n\\`policy new\\` also writes a gitignored \\`input.json\\` (copy of \\`input.example.json\\`)\nfor local Evaluate. \\`policy lint\\` / \\`policy test\\` default to every child with a\n\\`.manifest\\` and use bundle-mode \\`opa test -b\\`, so both input files can coexist\nwithout a merge error.\n\n## Layout\n\n\\`\\`\\`\ntranscend/policy/\n example-bundle/ # publish directory (added by policy new)\n example/ # package root (matches .manifest roots)\n input.example.json\n input.json # local only (gitignored)\n schemas/ # input JSON Schemas (editor + opa check -s)\n .regal/config.yaml\n\\`\\`\\`\n\nReplace or delete example bundles when you have a real policy. Publish each\nbundle separately (\\`transcend policy publish --bundle-name … <dir>\\`).\n\n## Bundle names and Permissions\n\nUpload does **not** distinguish bundle kinds. What makes a bundle “Permissions”\nis the remote name plus where Sombra queries it:\n\n- **Permissions API** always loads the fixed remote bundle name\n \\`${PERMISSIONS_POLICY_BUNDLE_NAME}\\`. Publish that starter with\n \\`--bundle-name=${PERMISSIONS_POLICY_BUNDLE_NAME}\\`.\n- **Generic / decide** bundles use any other \\`--bundle-name\\` and the decide\n path.\n\nScaffolded \\`.manifest\\` files may include \\`metadata.transcend.io.template\\`\n(\\`generic\\` or \\`permissions\\`) as an authoring hint. Upload ignores it;\n\\`policy publish\\` may warn when the hint and \\`--bundle-name\\` disagree.\n`;\n\n/** Workspace-relative path to the disposable example entrypoint. */\nexport const POLICY_STARTER_RESULT_REGO_PATH = `${POLICY_STARTER_BUNDLE_DIRECTORY}/${POLICY_STARTER_ROOT}/result/result.rego`;\n\n/** Workspace-relative path to the disposable example tests. */\nexport const POLICY_STARTER_RESULT_TEST_REGO_PATH = `${POLICY_STARTER_BUNDLE_DIRECTORY}/${POLICY_STARTER_ROOT}/result/result_test.rego`;\n\n/** Complete safe starter in deterministic plan order. */\nconst POLICY_STARTER_FILES: readonly PolicyStarterFile[] = [\n {\n path: `${POLICY_STARTER_BUNDLE_DIRECTORY}/${POLICY_MANIFEST_FILENAME}`,\n contents: POLICY_MANIFEST_TEMPLATE,\n description: 'Create the OPA bundle .manifest',\n },\n {\n path: '.regal/config.yaml',\n contents: POLICY_REGAL_CONFIG_TEMPLATE,\n description: `Pin strict Rego v1 linting to OPA ${POLICY_STARTER_OPA_VERSION} capabilities`,\n },\n {\n path: `schemas/${POLICY_STARTER_ROOT}/input.json`,\n contents: POLICY_INPUT_SCHEMA_TEMPLATE,\n description: 'Create the input JSON Schema for the example bundle',\n },\n {\n path: POLICY_STARTER_RESULT_REGO_PATH,\n contents: POLICY_RESULT_REGO_TEMPLATE,\n description: 'Create a documented fail-closed result entrypoint',\n },\n {\n path: POLICY_STARTER_RESULT_TEST_REGO_PATH,\n contents: POLICY_RESULT_TEST_REGO_TEMPLATE,\n description: 'Create executable tests for allow and fail-closed decisions',\n },\n {\n path: `${POLICY_STARTER_BUNDLE_DIRECTORY}/input.example.json`,\n contents: POLICY_INPUT_EXAMPLE_TEMPLATE,\n description: 'Create a sanitized local evaluation input',\n },\n {\n path: `${POLICY_STARTER_BUNDLE_DIRECTORY}/.gitignore`,\n contents: POLICY_GITIGNORE_TEMPLATE,\n description: 'Ignore only the private local input file',\n },\n {\n path: 'README.md',\n contents: POLICY_README_TEMPLATE,\n description: 'Create the concise policy project guide',\n },\n];\n\n/**\n * Generate an independent copy of the safe policy starter.\n *\n * @returns Deterministic relative files\n */\nexport function generatePolicyStarterFiles(): PolicyStarterFile[] {\n return POLICY_STARTER_FILES.map((file) => ({ ...file }));\n}\n\n// ---------------------------------------------------------------------------\n// Workspace-only init files (no bundles)\n// ---------------------------------------------------------------------------\n\n/** Files created by `policy init` — empty workspace only. */\nconst POLICY_INIT_WORKSPACE_FILES: readonly PolicyStarterFile[] = [\n {\n path: '.regal/config.yaml',\n contents: POLICY_REGAL_CONFIG_EMPTY_TEMPLATE,\n description: `Pin strict Rego v1 linting to OPA ${POLICY_STARTER_OPA_VERSION} capabilities`,\n },\n {\n path: 'README.md',\n contents: POLICY_README_TEMPLATE,\n description: 'Create the concise multi-bundle workspace guide',\n },\n];\n\n/**\n * Generate files for an empty workspace (init without bundles).\n *\n * @returns Deterministic workspace files\n */\nexport function generatePolicyWorkspaceFiles(): PolicyStarterFile[] {\n return POLICY_INIT_WORKSPACE_FILES.map((file) => ({ ...file }));\n}\n\n// ---------------------------------------------------------------------------\n// Policy bundle templates (used by `policy new`)\n// ---------------------------------------------------------------------------\n\n/** Interactive labels for each policy template. */\nexport const POLICY_TEMPLATE_PROMPT_LABELS: Record<PolicyTemplateName, string> = {\n generic: 'Generic example',\n permissions: 'Permission API starter',\n};\n\n/** Default root names for each template. */\nexport const POLICY_TEMPLATE_DEFAULT_ROOTS: Record<PolicyTemplateName, string> = {\n generic: 'example',\n permissions: PERMISSIONS_POLICY_BUNDLE_NAME,\n};\n\n/**\n * Build the bundle directory name from a root.\n *\n * @param root - Package root\n * @returns Bundle directory name\n */\nexport function buildBundleDirectoryName(root: string): string {\n return `${root}-bundle`;\n}\n\n/**\n * Generate bundle files for the generic template.\n *\n * @param root - Package root name\n * @returns Bundle files relative to the workspace\n */\nexport function generateGenericBundleFiles(root: string): PolicyStarterFile[] {\n const bundle = buildBundleDirectoryName(root);\n return [\n {\n path: `${bundle}/${POLICY_MANIFEST_FILENAME}`,\n contents: buildPolicyManifestTemplate(root, 'generic'),\n description: 'Create the OPA bundle .manifest',\n },\n {\n path: `schemas/${root}/input.json`,\n contents: `{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": \"https://transcend.io/policy-schemas/${root}/input.json\",\n \"title\": \"${root === 'example' ? 'Example policy input' : `${root} policy input`}\",\n \"description\": \"${root === 'example' ? 'Disposable teaching envelope for the example bundle.' : `Input envelope for the ${root} bundle.`}\",\n \"type\": \"object\",\n \"additionalProperties\": true,\n \"properties\": {\n \"subject\": {\n \"type\": \"object\",\n \"additionalProperties\": true,\n \"properties\": {\n \"trusted\": {\n \"type\": \"boolean\",\n \"description\": \"When true, the ${root === 'example' ? 'example' : root} policy allows; otherwise it denies.\"\n }\n }\n }\n }\n}\n`,\n description: `Create the input JSON Schema for the ${root} bundle`,\n },\n {\n path: `${bundle}/${root}/result/result.rego`,\n contents: `# METADATA\n# title: ${root === 'example' ? 'Disposable example result' : `${root} result`}\n# description: |\n# A fail-closed ${root === 'example' ? 'teaching ' : ''}entrypoint. Replace this ${root === 'example' ? 'example ' : ''}with the document\n# tree and result contract required by your application.\n# scope: package\n# schemas:\n# - input: schema.${root}.input\n# entrypoint: true\npackage ${root}.result\n\nimport rego.v1\n\ndefault decision := \"deny\"\n\ndecision := \"allow\" if {\n\\tinput.subject.trusted == true\n}\n\ndefault reason_code := \"untrusted_or_invalid_input\"\n\nreason_code := \"trusted_subject\" if {\n\\tdecision == \"allow\"\n}\n`,\n description: 'Create a documented fail-closed result entrypoint',\n },\n {\n path: `${bundle}/${root}/result/result_test.rego`,\n contents: `package ${root}.result_test\n\nimport data.${root}.result\nimport rego.v1\n\ntest_denies_untrusted_subject if {\n\\tresult.decision == \"deny\" with input as {\"subject\": {\"trusted\": false}}\n\\tresult.reason_code == \"untrusted_or_invalid_input\" with input as {\"subject\": {\"trusted\": false}}\n}\n\ntest_fails_closed_for_missing_subject if {\n\\tresult.decision == \"deny\" with input as {}\n\\tresult.reason_code == \"untrusted_or_invalid_input\" with input as {}\n}\n\ntest_allows_trusted_subject if {\n\\tresult.decision == \"allow\" with input as {\"subject\": {\"trusted\": true}}\n\\tresult.reason_code == \"trusted_subject\" with input as {\"subject\": {\"trusted\": true}}\n}\n`,\n description: 'Create executable tests for allow and fail-closed decisions',\n },\n {\n path: `${bundle}/input.example.json`,\n contents: POLICY_INPUT_EXAMPLE_TEMPLATE,\n description: 'Create a sanitized local evaluation input example',\n },\n {\n path: `${bundle}/input.json`,\n contents: POLICY_INPUT_EXAMPLE_TEMPLATE,\n description: 'Create a local evaluation input (gitignored copy of the example)',\n },\n {\n path: `${bundle}/.gitignore`,\n contents: POLICY_GITIGNORE_TEMPLATE,\n description: 'Ignore only the private local input file',\n },\n ];\n}\n\n/**\n * Generate bundle files for the permissions template.\n *\n * @param root - Package root name\n * @returns Bundle files relative to the workspace\n */\n/**\n * Serialize the published Permissions input schema for a local OPA schema path.\n *\n * OPA maps `schemas/{root}/input.json` → `schema.{root}.input`; the file is a\n * copy of the published schema so type-checking works offline.\n *\n * @returns Schema file contents ending in a trailing newline\n */\nexport function buildPermissionsInputSchemaContents(): string {\n return `${JSON.stringify(permissionsPolicyInputSchema, null, 2)}\\n`;\n}\n\n/**\n * Serialize the published Permissions input example for local evaluation.\n *\n * @returns Example input JSON ending in a trailing newline\n */\nexport function buildPermissionsInputExampleContents(): string {\n const [example] = permissionsPolicyInputSchema.examples;\n if (!example) {\n throw new Error('permissions-policy-input.json must declare at least one example');\n }\n return `${JSON.stringify(example, null, 2)}\\n`;\n}\n\n/**\n * Generate bundle files for the permissions template.\n *\n * @param root - Package root name\n * @returns Bundle files relative to the workspace\n */\nexport function generatePermissionsBundleFiles(root: string): PolicyStarterFile[] {\n const bundle = buildBundleDirectoryName(root);\n const inputExample = buildPermissionsInputExampleContents();\n return [\n {\n path: `${bundle}/${POLICY_MANIFEST_FILENAME}`,\n contents: buildPolicyManifestTemplate(root, 'permissions'),\n description: 'Create the OPA bundle .manifest',\n },\n {\n path: `schemas/${root}/input.json`,\n contents: buildPermissionsInputSchemaContents(),\n description: `Create the input JSON Schema for the ${root} bundle (from published permissions-policy-input.json)`,\n },\n {\n path: `${bundle}/${root}/config/config.rego`,\n contents: `# METADATA\n# title: Permissions config\n# description: |\n# Static bundle configuration. Values come from data.json in this directory.\npackage ${root}.config\n\nimport rego.v1\n`,\n description: 'Create the static bundle configuration package',\n },\n {\n path: `${bundle}/${root}/config/data.json`,\n contents: `{\n \"default_consent_allowed\": true,\n \"purposes\": [\n \"Analytics\",\n \"SaleOfInfo\",\n \"Marketing\",\n \"Personalization\"\n ],\n \"default_consent\": {}\n}\n`,\n description: 'Create the static purpose configuration data',\n },\n {\n path: `${bundle}/${root}/helpers/preference/preference.rego`,\n contents: `# METADATA\n# title: Purpose preference resolution\n# description: |\n# Resolves the subject's recorded choice for one purpose from the OPA\n# \\`input.preferences\\` array Sombra injects (one \\`{name, choice,\n# days_since_choice?}\\` entry per purpose; \\`choice\\` may be null when unset).\npackage ${root}.helpers.preference\n\nimport rego.v1\n\nimport data.${root}.config\n\nraw_preferences := object.get(input, \"preferences\", [])\n\nsource_available if {\n\\tis_array(raw_preferences)\n\\tevery entry in raw_preferences {\n\\t\\tis_object(entry)\n\\t\\tis_string(entry.name)\n\\t\\tentry.name != \"\"\n\\t}\n}\n\n# Boolean choice for a purpose, last-wins across duplicate entries (matching\n# Sombra's own dedup semantics). Returns the boolean only when a usable choice\n# exists; otherwise the rule is undefined and the purpose is undecided.\nchoice(name) := choice if {\n\\tsource_available\n\\tmatching := [entry | some entry in raw_preferences; entry.name == name]\n\\tcount(matching) > 0\n\\tlast := matching[count(matching) - 1]\n\\tchoice := object.get(last, \"choice\", null)\n\\tis_boolean(choice)\n}\n\nenabled(name) if choice(name) == true\n\ndisabled(name) if choice(name) == false\n\ndecided(name) if enabled(name)\n\ndecided(name) if disabled(name)\n\n# A configured purpose with no usable boolean choice is undecided: the subject\n# never chose, so the resolved default applies (not an opt-out).\nundecided(name) if {\n\\tname in config.purposes\n\\tnot enabled(name)\n\\tnot disabled(name)\n}\n`,\n description: 'Create purpose preference resolution helpers',\n },\n {\n path: `${bundle}/${root}/helpers/preference/preference_test.rego`,\n contents: `# Mechanical tests for purpose preference resolution.\n# Policy outcomes are tested beside the public entrypoint.\npackage ${root}.helpers.preference_test\n\nimport rego.v1\n\nimport data.${root}.helpers.preference\n\ngiven(preferences) := {\"preferences\": preferences}\n\ntest_treats_an_absent_preferences_array_as_no_recorded_choices if {\n\\tfixture := {}\n\n\\tpreference.source_available with input as fixture\n\\tpreference.undecided(\"Marketing\") with input as fixture\n}\n\ntest_accepts_an_empty_preferences_array if {\n\\tfixture := given([])\n\n\\tpreference.source_available with input as fixture\n\\tpreference.undecided(\"Marketing\") with input as fixture\n}\n\ntest_reports_a_non_array_preferences_as_unavailable if {\n\\tnot preference.source_available with input as {\"preferences\": {\"Marketing\": true}}\n}\n\ntest_reports_entries_without_a_usable_name_as_unavailable if {\n\\tnot preference.source_available with input as {\"preferences\": [{}]}\n\\tnot preference.source_available with input as {\"preferences\": [{\"name\": \"\"}]}\n\\tnot preference.source_available with input as {\"preferences\": [{\"choice\": true}]}\n}\n\ntest_distinguishes_enabled_and_disabled_choices if {\n\\tfixture := given([\n\\t\\t{\"name\": \"Marketing\", \"choice\": true},\n\\t\\t{\"name\": \"Analytics\", \"choice\": false},\n\\t])\n\n\\tpreference.enabled(\"Marketing\") with input as fixture\n\\tpreference.disabled(\"Analytics\") with input as fixture\n\\tpreference.decided(\"Marketing\") with input as fixture\n\\tpreference.decided(\"Analytics\") with input as fixture\n\\tnot preference.undecided(\"Marketing\") with input as fixture\n}\n\ntest_treats_a_null_choice_as_undecided if {\n\\tfixture := given([{\"name\": \"Marketing\", \"choice\": null}])\n\n\\tpreference.undecided(\"Marketing\") with input as fixture\n\\tnot preference.decided(\"Marketing\") with input as fixture\n}\n\ntest_treats_an_absent_choice_key_as_undecided if {\n\\tfixture := given([{\"name\": \"Marketing\", \"channel\": \"email\"}])\n\n\\tpreference.undecided(\"Marketing\") with input as fixture\n}\n\ntest_treats_a_non_boolean_choice_as_undecided if {\n\\tfixture := given([{\"name\": \"Personalization\", \"choice\": \"Daily\"}])\n\n\\tpreference.undecided(\"Personalization\") with input as fixture\n\\tnot preference.decided(\"Personalization\") with input as fixture\n}\n\n# Sombra dedupes by purpose before sending; last-wins keeps the bundle correct\n# against hand-crafted input that names the same purpose twice.\ntest_last_wins_on_duplicate_entries if {\n\\tfixture := given([\n\\t\\t{\"name\": \"SaleOfInfo\", \"choice\": true},\n\\t\\t{\"name\": \"SaleOfInfo\", \"choice\": false},\n\\t])\n\n\\tpreference.disabled(\"SaleOfInfo\") with input as fixture\n\\tnot preference.enabled(\"SaleOfInfo\") with input as fixture\n}\n\n# Extra fields on an entry (evidence, days_since_choice, channel) are ignored\n# rather than treated as a choice.\ntest_ignores_fields_beyond_the_recorded_choice if {\n\\tfixture := given([{\n\\t\\t\"name\": \"Marketing\",\n\\t\\t\"choice\": true,\n\\t\\t\"evidence\": \"consent_banner\",\n\\t\\t\"days_since_choice\": 45,\n\\t\\t\"channel\": \"email\",\n\\t}])\n\n\\tpreference.enabled(\"Marketing\") with input as fixture\n}\n`,\n description: 'Create preference resolution tests',\n },\n {\n path: `${bundle}/${root}/main.rego`,\n contents: `# METADATA\n# title: Permissions bundle root\n# description: |\n# Declares the Permissions envelope schema for this package and all\n# subpackages (helpers, purposes, and static config data).\n# scope: subpackages\n# schemas:\n# - input: schema.${root}.input\npackage ${root}\n\nimport rego.v1\n`,\n description: 'Create the bundle root package with schema declaration',\n },\n {\n path: `${bundle}/${root}/purposes/analytics/analytics.rego`,\n contents: `# METADATA\n# title: Analytics purpose decision\n# description: |\n# The Analytics purpose's verdict as a \\`result\\` document\n# (\\`data.${root}.purposes.analytics.result\\`).\npackage ${root}.purposes.analytics\n\nimport rego.v1\n\nimport data.${root}.helpers.preference\n\ndefault result := {\n\\t\"decision\": \"deny\",\n\\t\"reason_code\": \"default_deny\",\n}\n\nresult := {\n\\t\"decision\": \"allow\",\n\\t\"reason_code\": \"explicit_allow\",\n} if {\n\\tpreference.enabled(\"Analytics\")\n}\n\nresult := {\n\\t\"decision\": \"deny\",\n\\t\"reason_code\": \"explicit_deny\",\n} if {\n\\tpreference.disabled(\"Analytics\")\n}\n\nresult := {\n\\t\"decision\": \"allow\",\n\\t\"reason_code\": \"default_allow\",\n} if {\n\\tpreference.undecided(\"Analytics\")\n}\n`,\n description: 'Create the analytics purpose decision rule',\n },\n {\n path: `${bundle}/${root}/purposes/analytics/analytics_test.rego`,\n contents: `# Policy outcomes for the Analytics purpose (the queried document).\npackage ${root}.purposes.analytics_test\n\nimport rego.v1\n\nimport data.${root}.purposes.analytics\n\ngiven(preferences) := {\"preferences\": preferences}\n\ntest_explicit_allow if {\n\\tresult := analytics.result with input as given([{\"name\": \"Analytics\", \"choice\": true}])\n\\tresult == {\n\\t\\t\"decision\": \"allow\",\n\\t\\t\"reason_code\": \"explicit_allow\",\n\\t}\n}\n\ntest_explicit_deny if {\n\\tresult := analytics.result with input as given([{\"name\": \"Analytics\", \"choice\": false}])\n\\tresult == {\n\\t\\t\"decision\": \"deny\",\n\\t\\t\"reason_code\": \"explicit_deny\",\n\\t}\n}\n\ntest_undecided_resolves_to_default_allow if {\n\\tresult := analytics.result with input as given([])\n\\tresult == {\n\\t\\t\"decision\": \"allow\",\n\\t\\t\"reason_code\": \"default_allow\",\n\\t}\n}\n\ntest_missing_preferences_resolves_to_default_allow if {\n\\tresult := analytics.result with input as {}\n\\tresult == {\n\\t\\t\"decision\": \"allow\",\n\\t\\t\"reason_code\": \"default_allow\",\n\\t}\n}\n\ntest_non_boolean_choice_falls_back_to_default_allow if {\n\\tresult := analytics.result with input as given([{\"name\": \"Analytics\", \"choice\": \"yes\"}])\n\\tresult == {\n\\t\\t\"decision\": \"allow\",\n\\t\\t\"reason_code\": \"default_allow\",\n\\t}\n}\n\ntest_malformed_preferences_are_treated_as_undecided if {\n\\t# source_available is false, so enabled/disabled never fire; undecided still\n\\t# holds for configured purposes (not enabled and not disabled).\n\\tresult := analytics.result with input as {\"preferences\": {\"Analytics\": true}}\n\\tresult == {\n\\t\\t\"decision\": \"allow\",\n\\t\\t\"reason_code\": \"default_allow\",\n\\t}\n}\n`,\n description: 'Create analytics purpose decision tests',\n },\n {\n path: `${bundle}/${root}/purposes/entrypoint.rego`,\n contents: `# METADATA\n# title: Purpose decisions\n# description: Returns each purpose's decision and reason code.\n# scope: package\n# entrypoint: true\npackage ${root}.purposes\n\nimport rego.v1\n`,\n description: 'Create the purposes entrypoint',\n },\n {\n path: `${bundle}/input.example.json`,\n contents: inputExample,\n description: 'Create a sanitized permissions evaluation input example',\n },\n {\n path: `${bundle}/input.json`,\n contents: inputExample,\n description: 'Create a local evaluation input (gitignored copy of the example)',\n },\n {\n path: `${bundle}/.gitignore`,\n contents: POLICY_GITIGNORE_TEMPLATE,\n description: 'Ignore only the private local input file',\n },\n ];\n}\n\n/**\n * Generate bundle files for a given template and root.\n *\n * @param template - Template name\n * @param root - Package root name\n * @returns Bundle files relative to the workspace\n */\nexport function generatePolicyBundleFiles(\n template: PolicyTemplateName,\n root: string,\n): PolicyStarterFile[] {\n switch (template) {\n case 'generic':\n return generateGenericBundleFiles(root);\n case 'permissions':\n return generatePermissionsBundleFiles(root);\n default:\n throw new Error(`Unknown policy template: ${String(template)}`);\n }\n}\n","import { join, resolve } from 'node:path';\n\nimport type { LocalContext } from '../../context.js';\nimport {\n collectProjectRelativePaths,\n discoverProjectRepository,\n} from '../scaffolding/project-discovery.js';\nimport type { PolicyProjectState } from './policy-scaffold-model.js';\nimport {\n POLICY_MANIFEST_FILENAME,\n POLICY_STARTER_BUNDLE_DIRECTORY,\n} from './policy-scaffold-templates.js';\n\n/** Default local policy workspace directory (shared Regal + schemas). */\nexport const DEFAULT_POLICY_PROJECT_DIRECTORY = 'transcend/policy';\n\n/**\n * Resolve a selected policy project relative to the invocation directory.\n *\n * @param invocationDirectory - CLI working directory\n * @param directory - User-selected project directory\n * @returns Absolute policy project directory\n */\nexport function resolvePolicyProjectDirectory(\n invocationDirectory: string,\n directory: string = DEFAULT_POLICY_PROJECT_DIRECTORY,\n): string {\n return resolve(invocationDirectory, directory);\n}\n\n/**\n * Absolute path to the disposable starter publish directory under a workspace.\n *\n * @param workspaceDirectory - Absolute policy workspace directory\n * @returns Absolute starter bundle directory\n */\nexport function getPolicyStarterBundleDirectory(workspaceDirectory: string): string {\n return join(workspaceDirectory, POLICY_STARTER_BUNDLE_DIRECTORY);\n}\n\n/**\n * Resolve publishable bundle directories under a path.\n *\n * - If `directory` itself contains a `.manifest`, it is treated as one bundle.\n * - Otherwise immediate child directories that contain a `.manifest` are\n * returned in sorted order (multi-bundle workspace). Folder names are not\n * required to end in `-bundle`; that suffix is only the `policy new`\n * scaffolding convention.\n *\n * @param context - Filesystem-bearing CLI context\n * @param directory - Absolute workspace or bundle directory\n * @returns Absolute bundle directories to lint / test\n */\nexport function discoverPolicyBundleDirectories(\n context: Pick<LocalContext, 'fs'>,\n directory: string,\n): string[] {\n if (!context.fs.existsSync(directory) || !context.fs.statSync(directory).isDirectory()) {\n return [];\n }\n const manifestAtRoot = join(directory, POLICY_MANIFEST_FILENAME);\n if (context.fs.existsSync(manifestAtRoot) && context.fs.statSync(manifestAtRoot).isFile()) {\n return [directory];\n }\n return context.fs\n .readdirSync(directory, { withFileTypes: true })\n .filter((entry) => entry.isDirectory())\n .map((entry) => join(directory, entry.name))\n .filter((bundleDirectory) => {\n const manifestPath = join(bundleDirectory, POLICY_MANIFEST_FILENAME);\n return context.fs.existsSync(manifestPath) && context.fs.statSync(manifestPath).isFile();\n })\n .sort((left, right) => left.localeCompare(right));\n}\n\n/**\n * Collect repository state required by the pure policy planner.\n *\n * @param context - CLI context\n * @param directory - User-selected target directory\n * @returns Policy project discovery state\n */\nexport function discoverPolicyProject(\n context: LocalContext,\n directory: string = DEFAULT_POLICY_PROJECT_DIRECTORY,\n): PolicyProjectState {\n const invocationDirectory = context.process.cwd();\n const targetDirectory = resolvePolicyProjectDirectory(invocationDirectory, directory);\n if (\n context.fs.existsSync(targetDirectory) &&\n !context.fs.statSync(targetDirectory).isDirectory()\n ) {\n throw new Error(`Policy target is not a directory: ${targetDirectory}`);\n }\n const repository = discoverProjectRepository(context, {\n invocationDirectory,\n targetDirectory,\n standaloneProjectRoot: invocationDirectory,\n });\n return {\n invocationDirectory,\n targetDirectory,\n ...repository,\n relativePaths: collectProjectRelativePaths(context, targetDirectory),\n relativeFilePaths: collectProjectRelativePaths(context, targetDirectory, {\n includeDirectories: false,\n }),\n };\n}\n\n/**\n * Resolve the starter bundle manifest path for a discovered workspace.\n *\n * @param state - Policy project state\n * @returns Absolute manifest path under the starter `{root}-bundle/`\n */\nexport function getPolicyManifestPath(state: PolicyProjectState): string {\n return join(getPolicyStarterBundleDirectory(state.targetDirectory), POLICY_MANIFEST_FILENAME);\n}\n"],"mappings":"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"}
|