@aopslabs/aops 0.3.26 → 0.3.28

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.
@@ -112,6 +112,6 @@
112
112
  },
113
113
  "schemaVersion": 1,
114
114
  "standard": "aops-community-core-v1",
115
- "version": "0.3.26",
116
- "versionId": "community-core-0.3.26"
115
+ "version": "0.3.28",
116
+ "versionId": "community-core-0.3.28"
117
117
  }
@@ -10,8 +10,8 @@
10
10
  "name": "aops",
11
11
  "optional": false,
12
12
  "packageSha256": "bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb",
13
- "version": "0.3.26",
14
- "versionId": "community-core-0.3.26"
13
+ "version": "0.3.28",
14
+ "versionId": "community-core-0.3.28"
15
15
  },
16
16
  "gateway": {
17
17
  "byteLength": 621,
@@ -22,7 +22,7 @@
22
22
  "kind": "aops-community-agent-assets-inventory-v1",
23
23
  "schemaVersion": 1,
24
24
  "source": {
25
- "commit": "a27236e8340f10b98d86814e4cb25384ee68ffb4",
25
+ "commit": "4b74e2bab67b40ecc4ea37a972d2747491f7a8b2",
26
26
  "repository": "git+https://github.com/eeemzs/aops-platform",
27
27
  "root": "apps/aops-cli/assets/agent-assets"
28
28
  }
@@ -11,8 +11,8 @@
11
11
  "name": "aops",
12
12
  "optional": false,
13
13
  "packageSha256": "bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb",
14
- "version": "0.3.26",
15
- "versionId": "community-core-0.3.26"
14
+ "version": "0.3.28",
15
+ "versionId": "community-core-0.3.28"
16
16
  },
17
17
  "files": [
18
18
  {
@@ -118,7 +118,7 @@
118
118
  {
119
119
  "byteLength": 3811,
120
120
  "ref": "agent-assets/core/bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb/manifest.json",
121
- "sha256": "8b3d8fb2a3d05c995786293bc12bde3801d22b7f34fca45df3bcaeafd62408b8"
121
+ "sha256": "a7d0ddab5014eb99f6bae0b3b6ef74a5a8c85139a2c4d219c965f36c8e1bd807"
122
122
  },
123
123
  {
124
124
  "byteLength": 621,
@@ -128,7 +128,7 @@
128
128
  {
129
129
  "byteLength": 926,
130
130
  "ref": "agent-assets/inventory.json",
131
- "sha256": "a874074fae7653d3d93b505e048df25ce0b75f99f9bbf4b2c6fc9957583330bb"
131
+ "sha256": "6eb29fbf8c2399e854083ef13c2428440ce23f5088824877dc905a8520e47c1b"
132
132
  },
133
133
  {
134
134
  "byteLength": 281,
@@ -144,7 +144,7 @@
144
144
  },
145
145
  "inventory": {
146
146
  "ref": "agent-assets/inventory.json",
147
- "sha256": "a874074fae7653d3d93b505e048df25ce0b75f99f9bbf4b2c6fc9957583330bb"
147
+ "sha256": "6eb29fbf8c2399e854083ef13c2428440ce23f5088824877dc905a8520e47c1b"
148
148
  },
149
149
  "kind": "aops-community-agent-assets-release-v1",
150
150
  "projection": {
@@ -153,7 +153,7 @@
153
153
  },
154
154
  "root": "agent-assets",
155
155
  "schemaVersion": 1,
156
- "setSha256": "b5f5cf790144a78d213050b871f14bf45b2001ab0f1de9a73e890e20129244ba"
156
+ "setSha256": "02ea968111c9cdad8fe406cb8d9e57e6f26a58644ac193c04f00a6aa577ec3dc"
157
157
  },
158
158
  "evidence": {
159
159
  "signature": {
@@ -161,10 +161,10 @@
161
161
  }
162
162
  },
163
163
  "kind": "aops-community-agent-assets-bundle-v1",
164
- "releaseVersion": "0.3.26",
164
+ "releaseVersion": "0.3.28",
165
165
  "schemaVersion": 1,
166
166
  "source": {
167
- "commit": "a27236e8340f10b98d86814e4cb25384ee68ffb4",
167
+ "commit": "4b74e2bab67b40ecc4ea37a972d2747491f7a8b2",
168
168
  "repository": "git+https://github.com/eeemzs/aops-platform",
169
169
  "root": "apps/aops-cli/assets/agent-assets"
170
170
  }
@@ -4,13 +4,13 @@
4
4
  "x509CertificateChain": {
5
5
  "certificates": [
6
6
  {
7
- "rawBytes": "MIIHdDCCBvqgAwIBAgIUEb196+wUaaR63nO1Xvw3TOUD0ikwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjYwODIxMTQwNTMzWhcNMjYwODIxMTQxNTMzWjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEARMOkOFwduigLmt8MqXZMuAd0R4Zi8aTCHadoLM0z1Ot8tf1ag3XhBEd0dJ7wIZoLTI4ZrzgWE35z1uQMNED+KOCBhkwggYVMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUfAliGlZo9NdL6VE+WLS1+nHJPo4wHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wdAYDVR0RAQH/BGowaIZmaHR0cHM6Ly9naXRodWIuY29tL2VlZW16cy9hb3BzLXBsYXRmb3JtLy5naXRodWIvd29ya2Zsb3dzL3NpZ24tcHVibGljLWFnZW50LWFzc2V0cy55bWxAcmVmcy9oZWFkcy9tYWluMDkGCisGAQQBg78wAQEEK2h0dHBzOi8vdG9rZW4uYWN0aW9ucy5naXRodWJ1c2VyY29udGVudC5jb20wHwYKKwYBBAGDvzABAgQRd29ya2Zsb3dfZGlzcGF0Y2gwNgYKKwYBBAGDvzABAwQoYTI3MjM2ZTgzNDBmMTBiOThkODY4MTRlNGNiMjUzODRlZTY4ZmZiNDAxBgorBgEEAYO/MAEEBCNTaWduIGV4YWN0IHB1YmxpYyBBT1BTIGFnZW50IGFzc2V0czAiBgorBgEEAYO/MAEFBBRlZWVtenMvYW9wcy1wbGF0Zm9ybTAdBgorBgEEAYO/MAEGBA9yZWZzL2hlYWRzL21haW4wOwYKKwYBBAGDvzABCAQtDCtodHRwczovL3Rva2VuLmFjdGlvbnMuZ2l0aHVidXNlcmNvbnRlbnQuY29tMHYGCisGAQQBg78wAQkEaAxmaHR0cHM6Ly9naXRodWIuY29tL2VlZW16cy9hb3BzLXBsYXRmb3JtLy5naXRodWIvd29ya2Zsb3dzL3NpZ24tcHVibGljLWFnZW50LWFzc2V0cy55bWxAcmVmcy9oZWFkcy9tYWluMDgGCisGAQQBg78wAQoEKgwoYTI3MjM2ZTgzNDBmMTBiOThkODY4MTRlNGNiMjUzODRlZTY4ZmZiNDAdBgorBgEEAYO/MAELBA8MDWdpdGh1Yi1ob3N0ZWQwNwYKKwYBBAGDvzABDAQpDCdodHRwczovL2dpdGh1Yi5jb20vZWVlbXpzL2FvcHMtcGxhdGZvcm0wOAYKKwYBBAGDvzABDQQqDChhMjcyMzZlODM0MGYxMGI5OGQ4NjgxNGU0Y2IyNTM4NGVlNjhmZmI0MB8GCisGAQQBg78wAQ4EEQwPcmVmcy9oZWFkcy9tYWluMBoGCisGAQQBg78wAQ8EDAwKMTMxNjI1NTAyNjApBgorBgEEAYO/MAEQBBsMGWh0dHBzOi8vZ2l0aHViLmNvbS9lZWVtenMwGQYKKwYBBAGDvzABEQQLDAkxMDkzMTA3NzQwdgYKKwYBBAGDvzABEgRoDGZodHRwczovL2dpdGh1Yi5jb20vZWVlbXpzL2FvcHMtcGxhdGZvcm0vLmdpdGh1Yi93b3JrZmxvd3Mvc2lnbi1wdWJsaWMtYWdlbnQtYXNzZXRzLnltbEByZWZzL2hlYWRzL21haW4wOAYKKwYBBAGDvzABEwQqDChhMjcyMzZlODM0MGYxMGI5OGQ4NjgxNGU0Y2IyNTM4NGVlNjhmZmI0MCEGCisGAQQBg78wARQEEwwRd29ya2Zsb3dfZGlzcGF0Y2gwWwYKKwYBBAGDvzABFQRNDEtodHRwczovL2dpdGh1Yi5jb20vZWVlbXpzL2FvcHMtcGxhdGZvcm0vYWN0aW9ucy9ydW5zLzMyNDkwMTc4MDQ4L2F0dGVtcHRzLzEwFwYKKwYBBAGDvzABFgQJDAdwcml2YXRlMFIGCisGAQQBg78wARgERAxCcmVwbzplZWVtenNAMTA5MzEwNzc0L2FvcHMtcGxhdGZvcm1AMTMxNjI1NTAyNjpyZWY6cmVmcy9oZWFkcy9tYWluMIGKBgorBgEEAdZ5AgQCBHwEegB4AHYA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGgJKQrpwAABAMARzBFAiAbaPs/vGsC88x5Q6UoSXuJT3FYncHixlBVbmeQIoLlPAIhAPDUXNWfVapiJuqkIJbD0Z6aB8empQkwkquaXiD7EkAXMAoGCCqGSM49BAMDA2gAMGUCMC3q7OAnottyIkLBZM6tir+r4KBEzuLb3YuNmmm7LGL98zpzVfAc4tpCp5y74Cq8nQIxAPZRFbm6wkoSr5iWUqIVoNYeM9j8V+jvWOLJMEPp0temsAKgZTpPynnVaqd/8hWY4Q=="
7
+ "rawBytes": "MIIHdDCCBvugAwIBAgIUKz3TRlv3SyEK3rhBdC6XVPKLd6swCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjYwODIxMjEyMDMyWhcNMjYwODIxMjEzMDMyWjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEsdWUtMvNxb87LwPcaNwM+ylP9oVfnBeqdNEMwKKRzhu+wZWY8MaiqO2RpMY1rw6C2qW8eweTV4DNiaFS9ab5W6OCBhowggYWMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUJJHZBd+BtXs20lbN1levZtQde+swHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wdAYDVR0RAQH/BGowaIZmaHR0cHM6Ly9naXRodWIuY29tL2VlZW16cy9hb3BzLXBsYXRmb3JtLy5naXRodWIvd29ya2Zsb3dzL3NpZ24tcHVibGljLWFnZW50LWFzc2V0cy55bWxAcmVmcy9oZWFkcy9tYWluMDkGCisGAQQBg78wAQEEK2h0dHBzOi8vdG9rZW4uYWN0aW9ucy5naXRodWJ1c2VyY29udGVudC5jb20wHwYKKwYBBAGDvzABAgQRd29ya2Zsb3dfZGlzcGF0Y2gwNgYKKwYBBAGDvzABAwQoNGI3NGUyYmFiNjdiNDBlY2M0ZWEzN2E5NzJkMjc0NzQ5MWY3YThiMjAxBgorBgEEAYO/MAEEBCNTaWduIGV4YWN0IHB1YmxpYyBBT1BTIGFnZW50IGFzc2V0czAiBgorBgEEAYO/MAEFBBRlZWVtenMvYW9wcy1wbGF0Zm9ybTAdBgorBgEEAYO/MAEGBA9yZWZzL2hlYWRzL21haW4wOwYKKwYBBAGDvzABCAQtDCtodHRwczovL3Rva2VuLmFjdGlvbnMuZ2l0aHVidXNlcmNvbnRlbnQuY29tMHYGCisGAQQBg78wAQkEaAxmaHR0cHM6Ly9naXRodWIuY29tL2VlZW16cy9hb3BzLXBsYXRmb3JtLy5naXRodWIvd29ya2Zsb3dzL3NpZ24tcHVibGljLWFnZW50LWFzc2V0cy55bWxAcmVmcy9oZWFkcy9tYWluMDgGCisGAQQBg78wAQoEKgwoNGI3NGUyYmFiNjdiNDBlY2M0ZWEzN2E5NzJkMjc0NzQ5MWY3YThiMjAdBgorBgEEAYO/MAELBA8MDWdpdGh1Yi1ob3N0ZWQwNwYKKwYBBAGDvzABDAQpDCdodHRwczovL2dpdGh1Yi5jb20vZWVlbXpzL2FvcHMtcGxhdGZvcm0wOAYKKwYBBAGDvzABDQQqDCg0Yjc0ZTJiYWI2N2I0MGVjYzRlYTM3YTk3MmQyNzQ3NDkxZjdhOGIyMB8GCisGAQQBg78wAQ4EEQwPcmVmcy9oZWFkcy9tYWluMBoGCisGAQQBg78wAQ8EDAwKMTMxNjI1NTAyNjApBgorBgEEAYO/MAEQBBsMGWh0dHBzOi8vZ2l0aHViLmNvbS9lZWVtenMwGQYKKwYBBAGDvzABEQQLDAkxMDkzMTA3NzQwdgYKKwYBBAGDvzABEgRoDGZodHRwczovL2dpdGh1Yi5jb20vZWVlbXpzL2FvcHMtcGxhdGZvcm0vLmdpdGh1Yi93b3JrZmxvd3Mvc2lnbi1wdWJsaWMtYWdlbnQtYXNzZXRzLnltbEByZWZzL2hlYWRzL21haW4wOAYKKwYBBAGDvzABEwQqDCg0Yjc0ZTJiYWI2N2I0MGVjYzRlYTM3YTk3MmQyNzQ3NDkxZjdhOGIyMCEGCisGAQQBg78wARQEEwwRd29ya2Zsb3dfZGlzcGF0Y2gwWwYKKwYBBAGDvzABFQRNDEtodHRwczovL2dpdGh1Yi5jb20vZWVlbXpzL2FvcHMtcGxhdGZvcm0vYWN0aW9ucy9ydW5zLzMyNTI3OTU1NTExL2F0dGVtcHRzLzEwFwYKKwYBBAGDvzABFgQJDAdwcml2YXRlMFIGCisGAQQBg78wARgERAxCcmVwbzplZWVtenNAMTA5MzEwNzc0L2FvcHMtcGxhdGZvcm1AMTMxNjI1NTAyNjpyZWY6cmVmcy9oZWFkcy9tYWluMIGLBgorBgEEAdZ5AgQCBH0EewB5AHcA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGgJjJnNwAABAMASDBGAiEA1NThMhpMbIHSlcM1R1pZAUb+aMbHgfImv4eywNO+13MCIQCNDGdt/WC4r93RO2pbQFb2LZla78d+Y9aDRw7/evS6mjAKBggqhkjOPQQDAwNnADBkAjA2pREYTGp5wwefgH33D2euXopPmsY72LFbAd3ML7fN46JqfXyO1U188leQZbfeMrICMEpLodjpfLfJuKeLhF3pE6QtEJxau3rMvIfv4v0mFxf0M7kUybG59dt5OTENDNC88A=="
8
8
  }
9
9
  ]
10
10
  },
11
11
  "tlogEntries": [
12
12
  {
13
- "logIndex": "2549925838",
13
+ "logIndex": "2553771214",
14
14
  "logId": {
15
15
  "keyId": "wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="
16
16
  },
@@ -18,38 +18,38 @@
18
18
  "kind": "hashedrekord",
19
19
  "version": "0.0.1"
20
20
  },
21
- "integratedTime": "1787321134",
21
+ "integratedTime": "1787347232",
22
22
  "inclusionPromise": {
23
- "signedEntryTimestamp": "MEQCIDSncONAjgcVXpuzdBVGcqVA2vki7lnCIUdJeBvK6gcsAiBBgtUHbxZrko/DFrr18ROYgG02TkFx8AuDZdZNaR0Omw=="
23
+ "signedEntryTimestamp": "MEQCIDfcm9LkMQJMVzC1y8Zz+3W4lZCSfSEcqVcokF3cL9OQAiA2t3KDqtR5h61DxjgR/24n+diVWnRr1HroMr7gtnRX1g=="
24
24
  },
25
25
  "inclusionProof": {
26
- "logIndex": "2428021576",
27
- "rootHash": "+LKuQB3TIMAnL3LTYuTQXyJ64hc4uZ1KYTO3/vNHXio=",
28
- "treeSize": "2428021599",
26
+ "logIndex": "2431866952",
27
+ "rootHash": "FzfjDKMb7RwLCOsn5MqV4EI4I6He4TxvmsSWsJabXUo=",
28
+ "treeSize": "2431866964",
29
29
  "hashes": [
30
- "h9gh73O4rYSronb6GOz5syeHhUKi3C5GYjUY7oijQng=",
31
- "cV06tlyujelmONiBiqnx+QUxSEv+xQXpSHhHwPvqtWg=",
32
- "8WbeWbDsJCIOpzsADId06qcap5nc2YYD1Eyjuo/2rsw=",
33
- "JRib40hknq8xlLZbPitIzYWg5jfEO9mmmx68ey0KWJw=",
34
- "TpksfbpZ0gRiwlmr8/2QJcjJOiuyXNbKDncYYgTskOk=",
35
- "9r+Y38/eS4pEwIa6GhE155Ks0XRiaHNe1L9phasA/iI=",
36
- "Yzc/266OTwEZ5d/muSDENGaOICvYQt/UC4FrXRz4v/w=",
37
- "S4f5vTVavyEszlRDCqwxdBz/h39S+qNwDhc7Gc92IkI=",
38
- "Ryn1uPqRCd+R0LxxinHyyFbMs/d0zwL8GfigIwIeDlk=",
39
- "AvqFfY3iNK3nlPVO3rNJL+lkBfpB+1t5pKeoZvROrHM=",
40
- "1Uwz0cBmxMyigjaFyr60M4Nx+sjh+ZQeEV++chbZdx8=",
41
- "ZUPJSNagIo2Cuo9rPgcsX/Fhs+Siniykljq/4IU90q8=",
42
- "hqdrhjLzPeYTznZc4m+ZY2VU7KPmRsh8feBDuZIMWYE=",
43
- "zf+G623/SGJ6XTKZTBaFTZxRLfWHs31RLboxrrfLlkY=",
30
+ "TkmstTcLV80Z8cCC14nTYMmgLh+WDktBMOMgkpTc0BM=",
31
+ "ee0gHJOEFErA2/e5JNqBpVFZmpI9p/162qHYajKcUJ4=",
32
+ "LtPS7ZHwKewZie+9kcDo3GEi+K/OyYmhduazzRJBpwk=",
33
+ "yDZ7T4a4ZoMY72uid/bnC3pm3ukVkmrYVR15KKrmiks=",
34
+ "M07J7P95IA48Te8doVTxWxwMs9wCzLp1n4CivkC90ls=",
35
+ "Ifr+B23ijUQ8mSxGm2huQ33ngNbAkh45/6YVvJ6YkC8=",
36
+ "PDl2+2g31pAzh7inLT1wKZBgs8IQEH6UbNBtW6NlY/g=",
37
+ "Oy8zIaaZ4Jjs8LgOH36qYR5qfnXId5Ya/7xJobY3FRQ=",
38
+ "nb2F2HW8++pxo5kubryZYX4S4GpLlrAHfvhIygp7i48=",
39
+ "TajTE5zxXucFXS1a0NWZQSd8UUQtAjDtaQ47uEBlsAw=",
40
+ "LVkP/pXcdaXlrM7GmttJ9G/8UlAsG9peo4xRdefiIdE=",
41
+ "6pok3vcu/J36uan+mjVjYgeDF8aod94o/h6fjqQvlMA=",
42
+ "V26C/ZbgRB037k4IypmNXaS2AKEAq+mYdBpCYzUHZaI=",
43
+ "9573PZDNoVVnalVG+4BpJVrKEFneAwqHLpVdOEfbE0Y=",
44
44
  "RhRQJE8gq9XjV+FJY/OHh6ZhCnS6INvqNxL9MyobX54=",
45
45
  "SndbMKVtcTenAkwi2JBfGzD+mhexp1qJbRIY+A1JRIU=",
46
46
  "xH/DCseLHr9eKoYT8qsORZK7zVdEGYWHuVtsVrD95wY="
47
47
  ],
48
48
  "checkpoint": {
49
- "envelope": "rekor.sigstore.dev - 1193050959916656506\n2428021599\n+LKuQB3TIMAnL3LTYuTQXyJ64hc4uZ1KYTO3/vNHXio=\n\n— rekor.sigstore.dev wNI9ajBEAiA5tW+Maxzv0l/vjwqGBhYd6Bynuvy/mHoJWVKsU92U8QIgHo5kEBjVtzV9qt+WaQ67/YsYIkcFV2QonBVQCNfLOcA=\n"
49
+ "envelope": "rekor.sigstore.dev - 1193050959916656506\n2431866964\nFzfjDKMb7RwLCOsn5MqV4EI4I6He4TxvmsSWsJabXUo=\n\n— rekor.sigstore.dev wNI9ajBGAiEA3FKcKQyuF1Qinx8qvAOOpwvjarVMsXB66iDktb8uABkCIQDf7gkJs+AkOBETCt8Asn/QLbwZ65ZXdNgZsi95zCIMsQ==\n"
50
50
  }
51
51
  },
52
- "canonicalizedBody": "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiIwNmE3MWIyYTgwMWNhYTkzZTVhYzMwOTUwMmViYTJhZWM4ZTk5Njc1OTI4ODhkM2E1ZmQ2N2ZmMWFlMmNhNTUyIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FUUNJR3QxZENoQ1c4UDhFNEUybTNZOE1MMnVTaHFsbFJjK0ZCMmN4YjZCbDYvTUFpQjhyVlgvNFJMdGM3T0dCTUJVRHBYa2hyaUd2OTBpcTdHUDlKVW1uWDk4Tmc9PSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVaGtSRU5EUW5aeFowRjNTVUpCWjBsVlJXSXhPVFlyZDFWaFlWSTJNMjVQTVZoMmR6TlVUMVZFTUdscmQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcFpkMDlFU1hoTlZGRjNUbFJOZWxkb1kwNU5hbGwzVDBSSmVFMVVVWGhPVkUxNlYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVZCVWsxUGEwOUdkMlIxYVdkTWJYUTRUWEZZV2sxMVFXUXdValJhYVRoaFZFTklZV1FLYjB4Tk1Ib3hUM1E0ZEdZeFlXY3pXR2hDUldRd1pFbzNkMGxhYjB4VVNUUmFjbnBuVjBVek5Yb3hkVkZOVGtWRUswdFBRMEpvYTNkbloxbFdUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlZtUVd4cENrZHNXbTg1VG1STU5sWkZLMWRNVXpFcmJraEtVRzgwZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDJSQldVUldVakJTUVZGSUwwSkhiM2RoU1ZwdFlVaFNNR05JVFRaTWVUbHVZVmhTYjJSWFNYVlpNamwwVERKV2JGcFhNVFpqZVRsb1lqTkNlZ3BNV0VKeldWaFNiV0l6U25STWVUVnVZVmhTYjJSWFNYWmtNamw1WVRKYWMySXpaSHBNTTA1d1dqSTBkR05JVm1saVIyeHFURmRHYmxwWE5UQk1WMFo2Q21NeVZqQmplVFUxWWxkNFFXTnRWbTFqZVRsdldsZEdhMk41T1hSWlYyeDFUVVJyUjBOcGMwZEJVVkZDWnpjNGQwRlJSVVZMTW1nd1pFaENlazlwT0hZS1pFYzVjbHBYTkhWWlYwNHdZVmM1ZFdONU5XNWhXRkp2WkZkS01XTXlWbmxaTWpsMVpFZFdkV1JETldwaU1qQjNTSGRaUzB0M1dVSkNRVWRFZG5wQlFncEJaMUZTWkRJNWVXRXlXbk5pTTJSbVdrZHNlbU5IUmpCWk1tZDNUbWRaUzB0M1dVSkNRVWRFZG5wQlFrRjNVVzlaVkVrelRXcE5NbHBVWjNwT1JFSnRDazFVUW1sUFZHaHJUMFJaTkUxVVVteE9SMDVwVFdwVmVrOUVVbXhhVkZrMFdtMWFhVTVFUVhoQ1oyOXlRbWRGUlVGWlR5OU5RVVZGUWtOT1ZHRlhaSFVLU1VkV05GbFhUakJKU0VJeFdXMTRjRmw1UWtKVU1VSlVTVWRHYmxwWE5UQkpSMFo2WXpKV01HTjZRV2xDWjI5eVFtZEZSVUZaVHk5TlFVVkdRa0pTYkFwYVYxWjBaVzVOZGxsWE9YZGplVEYzWWtkR01GcHRPWGxpVkVGa1FtZHZja0puUlVWQldVOHZUVUZGUjBKQk9YbGFWMXA2VERKb2JGbFhVbnBNTWpGb0NtRlhOSGRQZDFsTFMzZFpRa0pCUjBSMmVrRkNRMEZSZEVSRGRHOWtTRkozWTNwdmRrd3pVblpoTWxaMVRHMUdhbVJIYkhaaWJrMTFXakpzTUdGSVZta0taRmhPYkdOdFRuWmlibEpzWW01UmRWa3lPWFJOU0ZsSFEybHpSMEZSVVVKbk56aDNRVkZyUldGQmVHMWhTRkl3WTBoTk5reDVPVzVoV0ZKdlpGZEpkUXBaTWpsMFRESldiRnBYTVRaamVUbG9Zak5DZWt4WVFuTlpXRkp0WWpOS2RFeDVOVzVoV0ZKdlpGZEpkbVF5T1hsaE1scHpZak5rZWt3elRuQmFNalIwQ21OSVZtbGlSMnhxVEZkR2JscFhOVEJNVjBaNll6SldNR041TlRWaVYzaEJZMjFXYldONU9XOWFWMFpyWTNrNWRGbFhiSFZOUkdkSFEybHpSMEZSVVVJS1p6YzRkMEZSYjBWTFozZHZXVlJKTTAxcVRUSmFWR2Q2VGtSQ2JVMVVRbWxQVkdoclQwUlpORTFVVW14T1IwNXBUV3BWZWs5RVVteGFWRmswV20xYWFRcE9SRUZrUW1kdmNrSm5SVVZCV1U4dlRVRkZURUpCT0UxRVYyUndaRWRvTVZscE1XOWlNMDR3V2xkUmQwNTNXVXRMZDFsQ1FrRkhSSFo2UVVKRVFWRndDa1JEWkc5a1NGSjNZM3B2ZGt3eVpIQmtSMmd4V1drMWFtSXlNSFphVjFac1lsaHdla3d5Um5aalNFMTBZMGQ0YUdSSFduWmpiVEIzVDBGWlMwdDNXVUlLUWtGSFJIWjZRVUpFVVZGeFJFTm9hRTFxWTNsTmVscHNUMFJOTUUxSFdYaE5SMGsxVDBkUk5FNXFaM2hPUjFVd1dUSkplVTVVVFRST1IxWnNUbXBvYlFwYWJVa3dUVUk0UjBOcGMwZEJVVkZDWnpjNGQwRlJORVZGVVhkUVkyMVdiV041T1c5YVYwWnJZM2s1ZEZsWGJIVk5RbTlIUTJselIwRlJVVUpuTnpoM0NrRlJPRVZFUVhkTFRWUk5lRTVxU1RGT1ZFRjVUbXBCY0VKbmIzSkNaMFZGUVZsUEwwMUJSVkZDUW5OTlIxZG9NR1JJUW5wUGFUaDJXakpzTUdGSVZta0tURzFPZG1KVE9XeGFWMVowWlc1TmQwZFJXVXRMZDFsQ1FrRkhSSFo2UVVKRlVWRk1SRUZyZUUxRWEzcE5WRUV6VG5wUmQyUm5XVXRMZDFsQ1FrRkhSQXAyZWtGQ1JXZFNiMFJIV205a1NGSjNZM3B2ZGt3eVpIQmtSMmd4V1drMWFtSXlNSFphVjFac1lsaHdla3d5Um5aalNFMTBZMGQ0YUdSSFduWmpiVEIyQ2t4dFpIQmtSMmd4V1drNU0ySXpTbkphYlhoMlpETk5kbU15Ykc1aWFURjNaRmRLYzJGWFRYUlpWMlJzWW01UmRGbFlUbnBhV0ZKNlRHNXNkR0pGUW5rS1dsZGFla3d5YUd4WlYxSjZUREl4YUdGWE5IZFBRVmxMUzNkWlFrSkJSMFIyZWtGQ1JYZFJjVVJEYUdoTmFtTjVUWHBhYkU5RVRUQk5SMWw0VFVkSk5RcFBSMUUwVG1wbmVFNUhWVEJaTWtsNVRsUk5ORTVIVm14T2FtaHRXbTFKTUUxRFJVZERhWE5IUVZGUlFtYzNPSGRCVWxGRlJYZDNVbVF5T1hsaE1scHpDbUl6WkdaYVIyeDZZMGRHTUZreVozZFhkMWxMUzNkWlFrSkJSMFIyZWtGQ1JsRlNUa1JGZEc5a1NGSjNZM3B2ZGt3eVpIQmtSMmd4V1drMWFtSXlNSFlLV2xkV2JHSlljSHBNTWtaMlkwaE5kR05IZUdoa1IxcDJZMjB3ZGxsWFRqQmhWemwxWTNrNWVXUlhOWHBNZWsxNVRrUnJkMDFVWXpSTlJGRTBUREpHTUFwa1IxWjBZMGhTZWt4NlJYZEdkMWxMUzNkWlFrSkJSMFIyZWtGQ1JtZFJTa1JCWkhkamJXd3lXVmhTYkUxR1NVZERhWE5IUVZGUlFtYzNPSGRCVW1kRkNsSkJlRU5qYlZaM1lucHdiRnBYVm5SbGJrNUJUVlJCTlUxNlJYZE9lbU13VERKR2RtTklUWFJqUjNob1pFZGFkbU50TVVGTlZFMTRUbXBKTVU1VVFYa0tUbXB3ZVZwWFdUWmpiVlp0WTNrNWIxcFhSbXRqZVRsMFdWZHNkVTFKUjB0Q1oyOXlRbWRGUlVGa1dqVkJaMUZEUWtoM1JXVm5RalJCU0ZsQk0xUXdkd3BoYzJKSVJWUktha2RTTkdOdFYyTXpRWEZLUzFoeWFtVlFTek12YURSd2VXZERPSEEzYnpSQlFVRkhaMHBMVVhKd2QwRkJRa0ZOUVZKNlFrWkJhVUZpQ21GUWN5OTJSM05ET0RoNE5WRTJWVzlUV0hWS1ZETkdXVzVqU0dsNGJFSldZbTFsVVVsdlRHeFFRVWxvUVZCRVZWaE9WMlpXWVhCcFNuVnhhMGxLWWtRS01GbzJZVUk0Wlcxd1VXdDNhM0YxWVZocFJEZEZhMEZZVFVGdlIwTkRjVWRUVFRRNVFrRk5SRUV5WjBGTlIxVkRUVU16Y1RkUFFXNXZkSFI1U1d0TVFncGFUVFowYVhJcmNqUkxRa1Y2ZFV4aU0xbDFUbTF0YlRkTVIwdzVPSHB3ZWxabVFXTTBkSEJEY0RWNU56UkRjVGh1VVVsNFFWQmFVa1ppYlRaM2EyOVRDbkkxYVZkVmNVbFdiMDVaWlUwNWFqaFdLMnAyVjA5TVNrMUZVSEF3ZEdWdGMwRkxaMXBVY0ZCNWJtNVdZWEZrTHpob1YxazBVVDA5Q2kwdExTMHRSVTVFSUVORlVsUkpSa2xEUVZSRkxTMHRMUzBLIn19fX0="
52
+ "canonicalizedBody": "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI0MjFjY2FhMGYwOTM3NDA3Zjc3MTMxOGM3ZjJlZmQyYjQxNDdhYmM5MjgyNmUwOGMxNmY0YWZjZWRjMDc0YmI3In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FWUNJUUNOU3pFMnhoanhEdHJVWUFJY3VEZjVHYmhCRFpISU9KazVlR3FLUzZxVWl3SWhBS1R3RVoyOFFTNlJmQXY2MkJWMSsrZUxOQ0Qxc1NLNmpqTzllWFpuV2VHQSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVaGtSRU5EUW5aMVowRjNTVUpCWjBsVlMzb3pWRkpzZGpOVGVVVkxNM0pvUW1SRE5saFdVRXRNWkRaemQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcFpkMDlFU1hoTmFrVjVUVVJOZVZkb1kwNU5hbGwzVDBSSmVFMXFSWHBOUkUxNVYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVZ6WkZkVmRFMTJUbmhpT0RkTWQxQmpZVTUzVFN0NWJGQTViMVptYmtKbGNXUk9SVTBLZDB0TFVucG9kU3QzV2xkWk9FMWhhWEZQTWxKd1RWa3hjbmMyUXpKeFZ6aGxkMlZVVmpSRVRtbGhSbE01WVdJMVZ6WlBRMEpvYjNkbloxbFhUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlZLU2toYUNrSmtLMEowV0hNeU1HeGlUakZzWlhaYWRGRmtaU3R6ZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDJSQldVUldVakJTUVZGSUwwSkhiM2RoU1ZwdFlVaFNNR05JVFRaTWVUbHVZVmhTYjJSWFNYVlpNamwwVERKV2JGcFhNVFpqZVRsb1lqTkNlZ3BNV0VKeldWaFNiV0l6U25STWVUVnVZVmhTYjJSWFNYWmtNamw1WVRKYWMySXpaSHBNTTA1d1dqSTBkR05JVm1saVIyeHFURmRHYmxwWE5UQk1WMFo2Q21NeVZqQmplVFUxWWxkNFFXTnRWbTFqZVRsdldsZEdhMk41T1hSWlYyeDFUVVJyUjBOcGMwZEJVVkZDWnpjNGQwRlJSVVZMTW1nd1pFaENlazlwT0hZS1pFYzVjbHBYTkhWWlYwNHdZVmM1ZFdONU5XNWhXRkp2WkZkS01XTXlWbmxaTWpsMVpFZFdkV1JETldwaU1qQjNTSGRaUzB0M1dVSkNRVWRFZG5wQlFncEJaMUZTWkRJNWVXRXlXbk5pTTJSbVdrZHNlbU5IUmpCWk1tZDNUbWRaUzB0M1dVSkNRVWRFZG5wQlFrRjNVVzlPUjBrelRrZFZlVmx0Um1sT2FtUnBDazVFUW14Wk1rMHdXbGRGZWs0eVJUVk9la3ByVFdwak1FNTZVVFZOVjFreldWUm9hVTFxUVhoQ1oyOXlRbWRGUlVGWlR5OU5RVVZGUWtOT1ZHRlhaSFVLU1VkV05GbFhUakJKU0VJeFdXMTRjRmw1UWtKVU1VSlVTVWRHYmxwWE5UQkpSMFo2WXpKV01HTjZRV2xDWjI5eVFtZEZSVUZaVHk5TlFVVkdRa0pTYkFwYVYxWjBaVzVOZGxsWE9YZGplVEYzWWtkR01GcHRPWGxpVkVGa1FtZHZja0puUlVWQldVOHZUVUZGUjBKQk9YbGFWMXA2VERKb2JGbFhVbnBNTWpGb0NtRlhOSGRQZDFsTFMzZFpRa0pCUjBSMmVrRkNRMEZSZEVSRGRHOWtTRkozWTNwdmRrd3pVblpoTWxaMVRHMUdhbVJIYkhaaWJrMTFXakpzTUdGSVZta0taRmhPYkdOdFRuWmlibEpzWW01UmRWa3lPWFJOU0ZsSFEybHpSMEZSVVVKbk56aDNRVkZyUldGQmVHMWhTRkl3WTBoTk5reDVPVzVoV0ZKdlpGZEpkUXBaTWpsMFRESldiRnBYTVRaamVUbG9Zak5DZWt4WVFuTlpXRkp0WWpOS2RFeDVOVzVoV0ZKdlpGZEpkbVF5T1hsaE1scHpZak5rZWt3elRuQmFNalIwQ21OSVZtbGlSMnhxVEZkR2JscFhOVEJNVjBaNll6SldNR041TlRWaVYzaEJZMjFXYldONU9XOWFWMFpyWTNrNWRGbFhiSFZOUkdkSFEybHpSMEZSVVVJS1p6YzRkMEZSYjBWTFozZHZUa2RKTTA1SFZYbFpiVVpwVG1wa2FVNUVRbXhaTWswd1dsZEZlazR5UlRWT2VrcHJUV3BqTUU1NlVUVk5WMWt6V1ZSb2FRcE5ha0ZrUW1kdmNrSm5SVVZCV1U4dlRVRkZURUpCT0UxRVYyUndaRWRvTVZscE1XOWlNMDR3V2xkUmQwNTNXVXRMZDFsQ1FrRkhSSFo2UVVKRVFWRndDa1JEWkc5a1NGSjNZM3B2ZGt3eVpIQmtSMmd4V1drMWFtSXlNSFphVjFac1lsaHdla3d5Um5aalNFMTBZMGQ0YUdSSFduWmpiVEIzVDBGWlMwdDNXVUlLUWtGSFJIWjZRVUpFVVZGeFJFTm5NRmxxWXpCYVZFcHBXVmRKTWs0eVNUQk5SMVpxV1hwU2JGbFVUVE5aVkdzelRXMVJlVTU2VVROT1JHdDRXbXBrYUFwUFIwbDVUVUk0UjBOcGMwZEJVVkZDWnpjNGQwRlJORVZGVVhkUVkyMVdiV041T1c5YVYwWnJZM2s1ZEZsWGJIVk5RbTlIUTJselIwRlJVVUpuTnpoM0NrRlJPRVZFUVhkTFRWUk5lRTVxU1RGT1ZFRjVUbXBCY0VKbmIzSkNaMFZGUVZsUEwwMUJSVkZDUW5OTlIxZG9NR1JJUW5wUGFUaDJXakpzTUdGSVZta0tURzFPZG1KVE9XeGFWMVowWlc1TmQwZFJXVXRMZDFsQ1FrRkhSSFo2UVVKRlVWRk1SRUZyZUUxRWEzcE5WRUV6VG5wUmQyUm5XVXRMZDFsQ1FrRkhSQXAyZWtGQ1JXZFNiMFJIV205a1NGSjNZM3B2ZGt3eVpIQmtSMmd4V1drMWFtSXlNSFphVjFac1lsaHdla3d5Um5aalNFMTBZMGQ0YUdSSFduWmpiVEIyQ2t4dFpIQmtSMmd4V1drNU0ySXpTbkphYlhoMlpETk5kbU15Ykc1aWFURjNaRmRLYzJGWFRYUlpWMlJzWW01UmRGbFlUbnBhV0ZKNlRHNXNkR0pGUW5rS1dsZGFla3d5YUd4WlYxSjZUREl4YUdGWE5IZFBRVmxMUzNkWlFrSkJSMFIyZWtGQ1JYZFJjVVJEWnpCWmFtTXdXbFJLYVZsWFNUSk9Na2t3VFVkV2FncFplbEpzV1ZSTk0xbFVhek5OYlZGNVRucFJNMDVFYTNoYWFtUm9UMGRKZVUxRFJVZERhWE5IUVZGUlFtYzNPSGRCVWxGRlJYZDNVbVF5T1hsaE1scHpDbUl6WkdaYVIyeDZZMGRHTUZreVozZFhkMWxMUzNkWlFrSkJSMFIyZWtGQ1JsRlNUa1JGZEc5a1NGSjNZM3B2ZGt3eVpIQmtSMmd4V1drMWFtSXlNSFlLV2xkV2JHSlljSHBNTWtaMlkwaE5kR05IZUdoa1IxcDJZMjB3ZGxsWFRqQmhWemwxWTNrNWVXUlhOWHBNZWsxNVRsUkpNMDlVVlRGT1ZFVjRUREpHTUFwa1IxWjBZMGhTZWt4NlJYZEdkMWxMUzNkWlFrSkJSMFIyZWtGQ1JtZFJTa1JCWkhkamJXd3lXVmhTYkUxR1NVZERhWE5IUVZGUlFtYzNPSGRCVW1kRkNsSkJlRU5qYlZaM1lucHdiRnBYVm5SbGJrNUJUVlJCTlUxNlJYZE9lbU13VERKR2RtTklUWFJqUjNob1pFZGFkbU50TVVGTlZFMTRUbXBKTVU1VVFYa0tUbXB3ZVZwWFdUWmpiVlp0WTNrNWIxcFhSbXRqZVRsMFdWZHNkVTFKUjB4Q1oyOXlRbWRGUlVGa1dqVkJaMUZEUWtnd1JXVjNRalZCU0dOQk0xUXdkd3BoYzJKSVJWUktha2RTTkdOdFYyTXpRWEZLUzFoeWFtVlFTek12YURSd2VXZERPSEEzYnpSQlFVRkhaMHBxU201T2QwRkJRa0ZOUVZORVFrZEJhVVZCQ2pGT1ZHaE5hSEJOWWtsSVUyeGpUVEZTTVhCYVFWVmlLMkZOWWtoblprbHRkalJsZVhkT1R5c3hNMDFEU1ZGRFRrUkhaSFF2VjBNMGNqa3pVazh5Y0dJS1VVWmlNa3hhYkdFM09HUXJXVGxoUkZKM055OWxkbE0yYldwQlMwSm5aM0ZvYTJwUFVGRlJSRUYzVG01QlJFSnJRV3BCTW5CU1JWbFVSM0ExZDNkbFpncG5TRE16UkRKbGRWaHZjRkJ0YzFrM01reEdZa0ZrTTAxTU4yWk9ORFpLY1daWWVVOHhWVEU0T0d4bFVWcGlabVZOY2tsRFRVVndURzlrYW5CbVRHWktDblZMWlV4b1JqTndSVFpSZEVWS2VHRjFNM0pOZGtsbWRqUjJNRzFHZUdZd1RUZHJWWGxpUnpVNVpIUTFUMVJGVGtST1F6ZzRRVDA5Q2kwdExTMHRSVTVFSUVORlVsUkpSa2xEUVZSRkxTMHRMUzBLIn19fX0="
53
53
  }
54
54
  ],
55
55
  "timestampVerificationData": {}
@@ -57,8 +57,8 @@
57
57
  "messageSignature": {
58
58
  "messageDigest": {
59
59
  "algorithm": "SHA2_256",
60
- "digest": "BqcbKoAcqpPlrDCVAuuirsjplnWSiI06X9Z/8a4spVI="
60
+ "digest": "QhzKoPCTdAf3cTGMfy79K0FHq8koJuCMFvSvztwHS7c="
61
61
  },
62
- "signature": "MEQCIGt1dChCW8P8E4E2m3Y8ML2uShqllRc+FB2cxb6Bl6/MAiB8rVX/4RLtc7OGBMBUDpXkhriGv90iq7GP9JUmnX98Ng=="
62
+ "signature": "MEYCIQCNSzE2xhjxDtrUYAIcuDf5GbhBDZHIOJk5eGqKS6qUiwIhAKTwEZ28QS6RfAv62BV1++eLNCD1sSK6jjO9eXZnWeGA"
63
63
  }
64
64
  }
@@ -1,3 +1,4 @@
1
+ import { spawnSync } from 'node:child_process';
1
2
  import { existsSync, lstatSync, mkdirSync, readdirSync, realpathSync, rmSync, } from 'node:fs';
2
3
  import os from 'node:os';
3
4
  import path from 'node:path';
@@ -17,6 +18,8 @@ import { inspectCommunityDoctor } from './community-doctor.js';
17
18
  import { buildCommunityInstanceContract, } from '../lib/community-instance-contract.js';
18
19
  import { asCommunityDiagnosticError } from '../lib/community-diagnostic.js';
19
20
  import { assertCommunityNativePathLayout, attestCommunityNativeExternalSnapshot, inspectCommunityNativeInstall, inspectCommunityNativeRuntime, loadExternalPostgresUrl, loadExternalServerAuthConfig, planCommunityNativeInstalledMigration, readCommunityNativeLogs, rollbackCommunityNativeApplication, resolveCommunityNativeInstalledNpmSourceRoot, resolveCommunityNativeLaunchMode, resolveCommunityNativePaths, setupCommunityNativeInstall, startCommunityNativeInstall, stopCommunityNativeInstall, } from '../lib/community-native-lifecycle.js';
21
+ import { inspectPgToolchain, locatePgTool, pgEnvironmentFromUrl } from '../lib/pg-tool-location.js';
22
+ import { readAopsServerEnvConfig } from '../utils/community-host-runtime.js';
20
23
  import { inspectCommunityNativeApplicationRecoveryStatus } from '../lib/community-native-application-recovery.js';
21
24
  import { inspectCommunityNativeDatabaseRecoveryStatus, restoreCommunityNativeDatabaseForUpdate, } from '../lib/community-native-database-recovery.js';
22
25
  import { inspectCommunityNativePostgres, removeCommunityNativePostgresContainerForReset, removeCommunityNativeManagedPostgres, } from '../lib/community-native-postgres.js';
@@ -265,6 +268,78 @@ function journalPathsForMode(options, runtimeMode) {
265
268
  function inspectFrom(options) {
266
269
  return inspectCommunityInstall(installSelection(options));
267
270
  }
271
+ const BACKUP_READINESS_CONTRACT = 'aops.backup-readiness/v1';
272
+ function computeCommunityBackupReadiness(options) {
273
+ const envelope = (body) => ({
274
+ schemaVersion: 1,
275
+ contract: BACKUP_READINESS_CONTRACT,
276
+ ...body,
277
+ });
278
+ const native = inspectNativeFrom(options);
279
+ const state = native.status === 'installed' ? native.state : undefined;
280
+ if (!state) {
281
+ const dialect = readAopsServerEnvConfig().repoDialect;
282
+ if (dialect === 'sqlite')
283
+ return envelope({ provider: 'no-host-backup', ready: true });
284
+ return envelope({
285
+ provider: 'unknown',
286
+ ready: false,
287
+ cause: `community_backup_readiness_state_unavailable:${native.status}`,
288
+ });
289
+ }
290
+ const postgres = state.postgres;
291
+ if (postgres.mode === 'container')
292
+ return envelope({ provider: 'managed-container', ready: true });
293
+ if (postgres.mode !== 'external' || !postgres.configRef || !postgres.tlsPolicy) {
294
+ return envelope({
295
+ provider: 'unknown',
296
+ ready: false,
297
+ cause: 'community_backup_readiness_postgres_state_incomplete',
298
+ });
299
+ }
300
+ let serverMajor;
301
+ try {
302
+ const url = loadExternalPostgresUrl(postgres.configRef, postgres.tlsPolicy);
303
+ const psql = locatePgTool('psql');
304
+ if (!psql) {
305
+ return envelope({ provider: 'unknown', ready: false, cause: 'community_backup_readiness_psql_missing' });
306
+ }
307
+ const probe = spawnSync(psql, ['--tuples-only', '--no-align', '--command', 'SHOW server_version_num'], {
308
+ encoding: 'utf8',
309
+ env: { ...process.env, ...pgEnvironmentFromUrl(String(url)) },
310
+ });
311
+ if (probe.error || probe.status !== 0) {
312
+ return envelope({ provider: 'unknown', ready: false, cause: 'community_backup_readiness_server_unreachable' });
313
+ }
314
+ const reported = Number(`${probe.stdout ?? ''}`.trim());
315
+ if (!Number.isFinite(reported) || reported <= 0) {
316
+ return envelope({ provider: 'unknown', ready: false, cause: 'community_backup_readiness_server_version_unreadable' });
317
+ }
318
+ serverMajor = Math.floor(reported / 10_000);
319
+ }
320
+ catch {
321
+ return envelope({ provider: 'unknown', ready: false, cause: 'community_backup_readiness_postgres_config_unusable' });
322
+ }
323
+ const readiness = inspectPgToolchain({
324
+ serverMajor,
325
+ run: (command, args) => {
326
+ const result = spawnSync(command, args, { encoding: 'utf8' });
327
+ if (result.error || result.status !== 0)
328
+ throw new Error('community_backup_readiness_tool_failed');
329
+ return `${result.stdout ?? ''}`;
330
+ },
331
+ });
332
+ return envelope({
333
+ provider: 'host-tools',
334
+ ready: readiness.ready,
335
+ serverMajor: readiness.requiredMajor,
336
+ tools: readiness.tools.map((evidence) => ({ tool: evidence.tool, major: evidence.major ?? null })),
337
+ ...(readiness.cause ? { cause: readiness.cause } : {}),
338
+ });
339
+ }
340
+ function runCommunityServerBackupReadiness(options) {
341
+ writeResult(computeCommunityBackupReadiness(options), true, options);
342
+ }
268
343
  function inspectNativeFrom(options) {
269
344
  return inspectCommunityNativeInstall(installSelection(options));
270
345
  }
@@ -2195,6 +2270,9 @@ export function makeCommunityServerCommand(identity = {}) {
2195
2270
  .action((options) => runCommunityServerRestart(options, dependencies));
2196
2271
  common(command.command('status').description('Show install and runtime status')).action(runCommunityServerStatus);
2197
2272
  common(command.command('health').description('Check the installed server health without mutation')).action(runCommunityServerHealth);
2273
+ common(command.command('backup-readiness')
2274
+ .description('Report whether this host can take the backup an update depends on, without mutation'))
2275
+ .action(runCommunityServerBackupReadiness);
2198
2276
  common(command.command('migration-plan').description('Read the exact pending native database migration plan without mutation'))
2199
2277
  .action((options) => runCommunityServerMigrationPlan(options, dependencies));
2200
2278
  common(command.command('attest-external-snapshot').description('Optionally record an operator-owned external PostgreSQL snapshot for one risky migration plan'))
@@ -128,6 +128,7 @@ function addSetupInitOptions(command) {
128
128
  return command
129
129
  .option('--path <path>', 'Setup path: 1 | 2 | 3 | 4 (semantic ids are also accepted)')
130
130
  .option('--postgres-config <path>', 'PostgreSQL env file override for path 1, or private output path for path 3')
131
+ .option('--postgres-bin-dir <path>', 'Directory holding PostgreSQL client tools you installed yourself; setup will not look for or install any')
131
132
  .option('--postgres-tls <policy>', 'PostgreSQL TLS policy: disable | require | verify-full')
132
133
  .option('--local-postgres-host <host>', 'Path 3 local PostgreSQL loopback host (default: 127.0.0.1)')
133
134
  .option('--local-postgres-port <port>', 'Path 3 local PostgreSQL port (default: 5432)', (value) => Number.parseInt(String(value), 10))
@@ -358,12 +358,118 @@ function defaultUpdateRoot(configRoot) {
358
358
  mkdirSync(root, { recursive: true, mode: 0o700 });
359
359
  return plainDirectory(root, 'AOPS config root');
360
360
  }
361
- export function prepareGlobalUpdate(options = {}, run = defaultRunner) {
361
+ function assertKnownDependencies(provided, known, label) {
362
+ for (const key of Object.keys(provided)) {
363
+ assert.ok(known.includes(key), `${label} received an unknown dependency: ${key}`);
364
+ }
365
+ }
366
+ const BACKUP_READINESS_CONTRACT = 'aops.backup-readiness/v1';
367
+ function resolveBackupProvider(run, entrypoint) {
368
+ let document;
369
+ try {
370
+ document = parseJson(run(process.execPath, [entrypoint, 'server', 'backup-readiness', '--json']), 'backup_readiness');
371
+ }
372
+ catch {
373
+ return { kind: 'unknown', reason: 'readiness_command_failed' };
374
+ }
375
+ if (document.contract !== BACKUP_READINESS_CONTRACT || document.schemaVersion !== 1) {
376
+ return { kind: 'unknown', reason: 'readiness_contract_unrecognised' };
377
+ }
378
+ const knownAnywhere = ['schemaVersion', 'contract', 'provider', 'ready', 'serverMajor', 'tools', 'cause'];
379
+ for (const key of Object.keys(document)) {
380
+ if (!knownAnywhere.includes(key))
381
+ return { kind: 'unknown', reason: 'readiness_document_unexpected_field' };
382
+ }
383
+ const exactKeys = (allowed) => {
384
+ const keys = Object.keys(document).sort();
385
+ return keys.length === allowed.length && allowed.slice().sort().every((key, index) => keys[index] === key);
386
+ };
387
+ const base = ['schemaVersion', 'contract', 'provider', 'ready'];
388
+ if (document.provider === 'managed-container' || document.provider === 'no-host-backup') {
389
+ if (!exactKeys(base) || document.ready !== true) {
390
+ return { kind: 'unknown', reason: 'readiness_document_variant_invalid' };
391
+ }
392
+ return { kind: document.provider === 'managed-container' ? 'managed-container' : 'no-host-backup' };
393
+ }
394
+ if (document.provider === 'unknown') {
395
+ if (!exactKeys([...base, 'cause']) || document.ready !== false || typeof document.cause !== 'string' ||
396
+ document.cause.length === 0) {
397
+ return { kind: 'unknown', reason: 'readiness_document_variant_invalid' };
398
+ }
399
+ return { kind: 'unknown', reason: `readiness_${document.cause}` };
400
+ }
401
+ if (document.provider !== 'host-tools') {
402
+ return { kind: 'unknown', reason: 'readiness_document_variant_invalid' };
403
+ }
404
+ const serverMajor = document.serverMajor;
405
+ if (!Number.isSafeInteger(serverMajor) || Number(serverMajor) <= 0) {
406
+ return { kind: 'unknown', reason: 'readiness_document_variant_invalid' };
407
+ }
408
+ const tools = document.tools;
409
+ if (!Array.isArray(tools) || tools.length !== 2) {
410
+ return { kind: 'unknown', reason: 'readiness_document_variant_invalid' };
411
+ }
412
+ const evidence = tools.map((entry) => entry);
413
+ const named = evidence.map((entry) => String(entry.tool)).sort();
414
+ if (named[0] !== 'pg_dump' || named[1] !== 'pg_restore') {
415
+ return { kind: 'unknown', reason: 'readiness_document_variant_invalid' };
416
+ }
417
+ for (const entry of evidence) {
418
+ if (Object.keys(entry).sort().join(',') !== 'major,tool') {
419
+ return { kind: 'unknown', reason: 'readiness_document_variant_invalid' };
420
+ }
421
+ }
422
+ const detail = `required_major_${String(serverMajor)} ` +
423
+ evidence.map((entry) => `${String(entry.tool)}=${entry.major === null ? 'missing' : String(entry.major)}`).join(' ');
424
+ if (document.ready === true) {
425
+ if (!exactKeys([...base, 'serverMajor', 'tools'])) {
426
+ return { kind: 'unknown', reason: 'readiness_document_variant_invalid' };
427
+ }
428
+ for (const entry of evidence) {
429
+ if (!Number.isSafeInteger(entry.major) || Number(entry.major) < Number(serverMajor)) {
430
+ return { kind: 'unknown', reason: 'readiness_document_variant_invalid' };
431
+ }
432
+ }
433
+ return { kind: 'host-tools', ready: true, detail };
434
+ }
435
+ if (document.ready !== false || !exactKeys([...base, 'serverMajor', 'tools', 'cause']) ||
436
+ typeof document.cause !== 'string' || document.cause.length === 0) {
437
+ return { kind: 'unknown', reason: 'readiness_document_variant_invalid' };
438
+ }
439
+ for (const entry of evidence) {
440
+ if (entry.major !== null && !Number.isSafeInteger(entry.major)) {
441
+ return { kind: 'unknown', reason: 'readiness_document_variant_invalid' };
442
+ }
443
+ }
444
+ return { kind: 'host-tools', ready: false, cause: document.cause, detail };
445
+ }
446
+ function assertBackupToolchainReady(resolveProvider) {
447
+ const provider = resolveProvider();
448
+ if (provider.kind === 'managed-container' || provider.kind === 'no-host-backup')
449
+ return false;
450
+ if (provider.kind === 'unknown') {
451
+ throw new Error(`community_pg_toolchain_server_major_unknown:${provider.reason}; the server host must let this ` +
452
+ 'command report whether it can back up its own database before an update that dumps it can begin');
453
+ }
454
+ if (!provider.ready) {
455
+ throw new Error(`${provider.cause ?? 'community_pg_toolchain_not_ready'}; the server host must provide PostgreSQL ` +
456
+ `client tools able to serve its own database before this server can be updated (${provider.detail})`);
457
+ }
458
+ return true;
459
+ }
460
+ function serverApplicationUpdateRequired(priorServer, targetServer) {
461
+ return priorServer !== targetServer;
462
+ }
463
+ export function prepareGlobalUpdate(options = {}, run = defaultRunner, dependencies = {}) {
464
+ assertKnownDependencies(dependencies, ['resolveBackupProvider'], 'prepareGlobalUpdate');
362
465
  const check = inspectGlobalUpdate(run);
363
466
  assert.equal(check.updateAvailable, true, 'aops_global_update_not_available');
364
467
  const installed = inspectGlobalInstall(run);
365
468
  const services = serviceSnapshot(run, installed.cliPath);
366
469
  assertHealthyServices(services);
470
+ if (serverApplicationUpdateRequired(installed.packages.server, check.available.server)) {
471
+ assertBackupToolchainReady(dependencies.resolveBackupProvider ?? (() => resolveBackupProvider(run, installed.cliPath)));
472
+ }
367
473
  const configRoot = defaultUpdateRoot(options.configRoot);
368
474
  const updatesRoot = path.join(configRoot, 'global-updates');
369
475
  mkdirSync(updatesRoot, { recursive: true, mode: 0o700 });
@@ -1108,6 +1214,7 @@ export async function recoverGlobalUpdate(options, run = defaultRunner, executio
1108
1214
  }
1109
1215
  }
1110
1216
  export async function applyGlobalUpdate(options, run = defaultRunner, execution = defaultExecutionDependencies) {
1217
+ assertKnownDependencies(execution, ['fetch', 'sleep', 'now', 'resolveBackupProvider'], 'applyGlobalUpdate');
1111
1218
  const plan = readGlobalUpdatePlan(options.plan);
1112
1219
  assert.equal(options.confirm, plan.planId, 'global update confirmation does not match the exact plan');
1113
1220
  assert.equal(existsSync(path.join(plan.operationRoot, RESULT_FILE)), false, 'global_update_plan_already_terminal');
@@ -1117,6 +1224,12 @@ export async function applyGlobalUpdate(options, run = defaultRunner, execution
1117
1224
  assert.equal(current.globalPrefix, plan.globalPrefix, 'global npm prefix changed after update planning');
1118
1225
  assertHealthyServices(serviceSnapshot(run, current.cliPath));
1119
1226
  const events = new GlobalUpdateEventLog(execution.now ?? (() => new Date()));
1227
+ if (serverApplicationUpdateRequired(plan.prior.packages.server, plan.target.packages.server)) {
1228
+ const candidateEntrypoint = path.join(plan.operationRoot, 'executor', 'node_modules', '@aopslabs', 'aops', 'dist', 'main.js');
1229
+ if (assertBackupToolchainReady(execution.resolveBackupProvider ?? (() => resolveBackupProvider(run, candidateEntrypoint)))) {
1230
+ events.push('verified:backup-toolchain');
1231
+ }
1232
+ }
1120
1233
  let applicationUpdateId = null;
1121
1234
  let applicationUpdateAttempted = false;
1122
1235
  let failedStage = 'stop-cockpit';
@@ -1133,7 +1246,7 @@ export async function applyGlobalUpdate(options, run = defaultRunner, execution
1133
1246
  const updated = inspectGlobalInstall(run);
1134
1247
  assert.deepEqual(updated.packages, plan.target.packages, 'installed candidate closure is not exact');
1135
1248
  assert.deepEqual(updated.topology, plan.target.topology, 'installed candidate topology is not canonical');
1136
- if (plan.prior.packages.server === plan.target.packages.server) {
1249
+ if (!serverApplicationUpdateRequired(plan.prior.packages.server, plan.target.packages.server)) {
1137
1250
  failedStage = 'start-unchanged-server';
1138
1251
  startService(run, updated.cliPath, 'server');
1139
1252
  events.push('started:server-unchanged');
@@ -543,9 +543,9 @@ function defaultNativeRoot() {
543
543
  return moduleDirectory.endsWith(`${path.sep}dist`) ? bundled : compiledOrSource;
544
544
  }
545
545
  function injectedNativeManifestSha256() {
546
- return typeof "d079a8a4ff6a05567912c963ac82f52259e980fafc22a041e4934932cdf7c2dd" === 'undefined'
546
+ return typeof "50600a98d6f7643b0dd62d7f6fb9c9761a8d384210dda72b10d4bbb761d3e14a" === 'undefined'
547
547
  ? undefined
548
- : "d079a8a4ff6a05567912c963ac82f52259e980fafc22a041e4934932cdf7c2dd";
548
+ : "50600a98d6f7643b0dd62d7f6fb9c9761a8d384210dda72b10d4bbb761d3e14a";
549
549
  }
550
550
  function sha256(bytes) {
551
551
  return createHash('sha256').update(bytes).digest('hex');
@@ -3,6 +3,8 @@ import { closeSync, constants, existsSync, lstatSync, mkdirSync, openSync, readF
3
3
  import path from 'node:path';
4
4
  import { randomUUID } from 'node:crypto';
5
5
  import { assertBackupUnchanged, hashHeldFile, openBackupForVerify } from './community-migration-snapshot.js';
6
+ import { pgEnvironmentFromUrl, resolvePgTool } from './pg-tool-location.js';
7
+ export { pgEnvironmentFromUrl, resolvePgTool };
6
8
  export async function runNativePgProcess(params) {
7
9
  return await new Promise((resolve, reject) => {
8
10
  const child = spawn(params.tool, params.args, {
@@ -18,54 +20,6 @@ export async function runNativePgProcess(params) {
18
20
  child.on('close', (exitCode, signal) => resolve({ exitCode, signal }));
19
21
  });
20
22
  }
21
- export function pgEnvironmentFromUrl(repoUrl) {
22
- let parsed;
23
- try {
24
- parsed = new URL(repoUrl);
25
- }
26
- catch {
27
- throw new Error('community_native_backup_repo_url_invalid');
28
- }
29
- if (parsed.protocol !== 'postgres:' && parsed.protocol !== 'postgresql:') {
30
- throw new Error('community_native_backup_repo_url_invalid');
31
- }
32
- const database = decodeURIComponent(parsed.pathname.replace(/^\//, ''));
33
- if (!database)
34
- throw new Error('community_native_backup_repo_url_invalid');
35
- const env = {
36
- PGHOST: parsed.hostname,
37
- PGDATABASE: database,
38
- };
39
- if (parsed.port)
40
- env.PGPORT = parsed.port;
41
- if (parsed.username)
42
- env.PGUSER = decodeURIComponent(parsed.username);
43
- if (parsed.password)
44
- env.PGPASSWORD = decodeURIComponent(parsed.password);
45
- const sslmode = parsed.searchParams.get('sslmode');
46
- if (sslmode)
47
- env.PGSSLMODE = sslmode;
48
- return env;
49
- }
50
- export function resolvePgTool(tool, options = {}) {
51
- const exists = options.exists ?? existsSync;
52
- const executable = process.platform === 'win32' ? `${tool}.exe` : tool;
53
- if (options.explicit)
54
- return options.explicit;
55
- if (options.binDir)
56
- return path.join(options.binDir, executable);
57
- const candidates = process.platform === 'darwin'
58
- ? ['/Applications/pgAdmin 4.app/Contents/SharedSupport', '/opt/homebrew/bin', '/usr/local/bin']
59
- : process.platform === 'win32'
60
- ? ['C:\\Program Files\\PostgreSQL\\17\\bin', 'C:\\Program Files\\PostgreSQL\\16\\bin']
61
- : ['/usr/bin', '/usr/local/bin'];
62
- for (const dir of candidates) {
63
- const candidate = path.join(dir, executable);
64
- if (exists(candidate))
65
- return candidate;
66
- }
67
- return executable;
68
- }
69
23
  function assertSafeBackupRoot(backupRoot) {
70
24
  mkdirSync(backupRoot, { recursive: true, mode: 0o700 });
71
25
  const stats = lstatSync(backupRoot, { bigint: true });
@@ -0,0 +1,90 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ export function distributionCodename(readOsRelease = () => readFileSync('/etc/os-release', 'utf8')) {
4
+ let content;
5
+ try {
6
+ content = readOsRelease();
7
+ }
8
+ catch {
9
+ return undefined;
10
+ }
11
+ const match = /^VERSION_CODENAME=("?)([a-z][a-z0-9]*)\1\s*$/mu.exec(content);
12
+ return match?.[2];
13
+ }
14
+ export function planPgClientAcquisition(major, options = {}) {
15
+ const platform = options.platform ?? process.platform;
16
+ const hasCommand = options.hasCommand ?? (() => true);
17
+ if (!Number.isInteger(major) || major <= 0)
18
+ return undefined;
19
+ if (platform === 'darwin') {
20
+ if (!hasCommand('brew'))
21
+ return undefined;
22
+ const args = ['install', `postgresql@${major}`];
23
+ return {
24
+ platform,
25
+ major,
26
+ steps: [{ command: 'brew', args }],
27
+ display: `brew ${args.join(' ')}`,
28
+ needsPrivilege: false,
29
+ };
30
+ }
31
+ if (platform === 'win32') {
32
+ if (!hasCommand('winget'))
33
+ return undefined;
34
+ const args = ['install', '--exact', '--silent', `PostgreSQL.PostgreSQL.${major}`];
35
+ return {
36
+ platform,
37
+ major,
38
+ steps: [{ command: 'winget', args }],
39
+ display: `winget ${args.join(' ')}`,
40
+ needsPrivilege: false,
41
+ };
42
+ }
43
+ if (!hasCommand('apt-get'))
44
+ return undefined;
45
+ const keyPath = '/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc';
46
+ const codename = options.codename ?? distributionCodename();
47
+ if (!codename)
48
+ return undefined;
49
+ const listLine = `deb [signed-by=${keyPath}] https://apt.postgresql.org/pub/repos/apt ${codename}-pgdg main`;
50
+ return {
51
+ platform,
52
+ major,
53
+ steps: [
54
+ { command: 'apt-get', args: ['update'] },
55
+ { command: 'apt-get', args: ['install', '-y', 'curl', 'ca-certificates', 'gnupg'] },
56
+ { command: 'install', args: ['-d', '/usr/share/postgresql-common/pgdg'] },
57
+ { command: 'curl', args: ['-fsSL', '-o', keyPath, 'https://www.postgresql.org/media/keys/ACCC4CF8.asc'] },
58
+ { command: 'sh', args: ['-c', `echo '${listLine}' > /etc/apt/sources.list.d/pgdg.list`] },
59
+ { command: 'apt-get', args: ['update'] },
60
+ { command: 'apt-get', args: ['install', '-y', `postgresql-client-${major}`] },
61
+ ],
62
+ display: `sudo apt-get install -y postgresql-client-${major} (after adding apt.postgresql.org)`,
63
+ needsPrivilege: true,
64
+ };
65
+ }
66
+ export function acquirePgClient(options) {
67
+ const plan = planPgClientAcquisition(options.major, {
68
+ platform: options.platform,
69
+ hasCommand: options.hasCommand,
70
+ ...(options.codename ? { codename: options.codename } : {}),
71
+ });
72
+ if (!plan)
73
+ return { status: 'unavailable', major: options.major };
74
+ const privileged = options.isPrivileged ?? (() => typeof process.getuid === 'function' && process.getuid() === 0);
75
+ if (plan.needsPrivilege && !privileged()) {
76
+ return { status: 'failed', plan, reason: 'pg_client_acquisition_requires_privilege' };
77
+ }
78
+ try {
79
+ for (const step of plan.steps)
80
+ options.run(step.command, step.args);
81
+ }
82
+ catch (error) {
83
+ return { status: 'failed', plan, reason: error instanceof Error ? error.message : 'pg_client_acquisition_failed' };
84
+ }
85
+ return { status: 'acquired', plan };
86
+ }
87
+ export function explicitPgBinDir(input) {
88
+ const value = String(input ?? '').trim();
89
+ return value.length > 0 ? path.resolve(value) : undefined;
90
+ }
@@ -0,0 +1,148 @@
1
+ import { existsSync, readdirSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ function windowsClientDirectories(listDirectory) {
4
+ const root = 'C:\\Program Files\\PostgreSQL';
5
+ const list = listDirectory ?? ((directory) => {
6
+ try {
7
+ return readdirSync(directory);
8
+ }
9
+ catch {
10
+ return [];
11
+ }
12
+ });
13
+ return list(root)
14
+ .map((entry) => ({ entry, major: Number(entry) }))
15
+ .filter((candidate) => Number.isInteger(candidate.major) && candidate.major > 0)
16
+ .sort((left, right) => right.major - left.major)
17
+ .map((candidate) => `${root}\\${candidate.entry}\\bin`);
18
+ }
19
+ export function pgToolCandidates(tool, options = {}) {
20
+ const platform = options.platform ?? process.platform;
21
+ const executable = platform === 'win32' ? `${tool}.exe` : tool;
22
+ const directories = platform === 'darwin'
23
+ ? ['/Applications/pgAdmin 4.app/Contents/SharedSupport', '/opt/homebrew/bin', '/usr/local/bin']
24
+ : platform === 'win32'
25
+ ? windowsClientDirectories(options.listDirectory)
26
+ : ['/usr/bin', '/usr/local/bin'];
27
+ return [executable, ...directories.map((directory) => path.join(directory, executable))];
28
+ }
29
+ export function resolvePgTool(tool, options = {}) {
30
+ const exists = options.exists ?? existsSync;
31
+ const platform = options.platform ?? process.platform;
32
+ const executable = platform === 'win32' ? `${tool}.exe` : tool;
33
+ if (options.explicit)
34
+ return options.explicit;
35
+ if (options.binDir)
36
+ return path.join(options.binDir, executable);
37
+ const candidates = platform === 'darwin'
38
+ ? ['/Applications/pgAdmin 4.app/Contents/SharedSupport', '/opt/homebrew/bin', '/usr/local/bin']
39
+ : platform === 'win32'
40
+ ? windowsClientDirectories(options.listDirectory)
41
+ : ['/usr/bin', '/usr/local/bin'];
42
+ for (const dir of candidates) {
43
+ const candidate = path.join(dir, executable);
44
+ if (exists(candidate))
45
+ return candidate;
46
+ }
47
+ return executable;
48
+ }
49
+ export function locatePgDump(env = process.env, exists = existsSync) {
50
+ return locatePgTool('pg_dump', env, exists);
51
+ }
52
+ export function locatePgTool(tool, env = process.env, exists = existsSync) {
53
+ const resolved = resolvePgTool(tool, { exists });
54
+ if (resolved.includes(path.sep))
55
+ return resolved;
56
+ const searchPath = (env.PATH ?? '').split(path.delimiter).filter((entry) => entry.length > 0);
57
+ for (const entry of searchPath) {
58
+ const candidate = path.join(entry, resolved);
59
+ if (exists(candidate))
60
+ return candidate;
61
+ }
62
+ return undefined;
63
+ }
64
+ export function pgEnvironmentFromUrl(repoUrl) {
65
+ let parsed;
66
+ try {
67
+ parsed = new URL(repoUrl);
68
+ }
69
+ catch {
70
+ throw new Error('community_native_backup_repo_url_invalid');
71
+ }
72
+ if (parsed.protocol !== 'postgres:' && parsed.protocol !== 'postgresql:') {
73
+ throw new Error('community_native_backup_repo_url_invalid');
74
+ }
75
+ const database = decodeURIComponent(parsed.pathname.replace(/^\//, ''));
76
+ if (!database)
77
+ throw new Error('community_native_backup_repo_url_invalid');
78
+ const env = {
79
+ PGHOST: parsed.hostname,
80
+ PGDATABASE: database,
81
+ };
82
+ if (parsed.port)
83
+ env.PGPORT = parsed.port;
84
+ if (parsed.username)
85
+ env.PGUSER = decodeURIComponent(parsed.username);
86
+ if (parsed.password)
87
+ env.PGPASSWORD = decodeURIComponent(parsed.password);
88
+ const sslmode = parsed.searchParams.get('sslmode');
89
+ if (sslmode)
90
+ env.PGSSLMODE = sslmode;
91
+ return env;
92
+ }
93
+ export function parsePgToolMajor(reported) {
94
+ const line = reported.trim();
95
+ const marked = /\(PostgreSQL\)\s+(\d+)/i.exec(line);
96
+ const fallback = /(?:^|\s)(\d+)\.\d+/.exec(line);
97
+ const major = Number((marked ?? fallback)?.[1] ?? Number.NaN);
98
+ return Number.isInteger(major) && major > 0 ? major : undefined;
99
+ }
100
+ export function inspectPgToolchain(options) {
101
+ const requiredMajor = options.serverMajor;
102
+ const tools = [];
103
+ let cause;
104
+ for (const tool of ['pg_dump', 'pg_restore']) {
105
+ let located;
106
+ let major;
107
+ for (const candidate of pgToolCandidates(tool, { platform: options.platform })) {
108
+ let reported;
109
+ try {
110
+ reported = options.run(candidate, ['--version']);
111
+ }
112
+ catch {
113
+ continue;
114
+ }
115
+ const parsed = parsePgToolMajor(reported);
116
+ located = candidate;
117
+ if (parsed !== undefined) {
118
+ major = parsed;
119
+ break;
120
+ }
121
+ }
122
+ if (located === undefined) {
123
+ tools.push({ tool });
124
+ cause ??= `community_pg_toolchain_missing:${tool}:required_major_${requiredMajor}`;
125
+ continue;
126
+ }
127
+ tools.push({ tool, path: located, major });
128
+ if (major === undefined) {
129
+ cause ??= `community_pg_toolchain_unusable:${tool}:required_major_${requiredMajor}`;
130
+ continue;
131
+ }
132
+ if (major < requiredMajor) {
133
+ cause ??= `community_pg_toolchain_major_too_old:${tool}:found_${major}:required_${requiredMajor}`;
134
+ }
135
+ }
136
+ return { requiredMajor, tools, ready: cause === undefined, cause };
137
+ }
138
+ export function assertPgToolchainReady(options) {
139
+ const readiness = inspectPgToolchain(options);
140
+ if (readiness.ready)
141
+ return readiness;
142
+ const detail = readiness.tools
143
+ .map((evidence) => `${evidence.tool}=${evidence.major ?? (evidence.path ? 'unusable' : 'missing')}`)
144
+ .join(' ');
145
+ throw new Error(`${readiness.cause}; the server host must provide PostgreSQL client tools of major ` +
146
+ `${readiness.requiredMajor} or newer -- the major this host's own database reports -- ` +
147
+ `before this server can be updated (${detail})`);
148
+ }
@@ -1,3 +1,6 @@
1
+ import { acquirePgClient, explicitPgBinDir } from './pg-client-acquisition.js';
2
+ import { inspectPgToolchain } from './pg-tool-location.js';
3
+ import { spawnSync } from 'node:child_process';
1
4
  import path from 'node:path';
2
5
  import { logInfo, logSuccess, logWarn } from '@aopslabs/xf-cli-ui';
3
6
  import { canonicalCommercialJson, managedInstallationLayoutBinding, sha256Commercial, } from '@aopslabs/artifact-trust-contracts';
@@ -197,6 +200,51 @@ function resolveOfficialCatalogReleasePath(value) {
197
200
  }
198
201
  return path.resolve(normalized);
199
202
  }
203
+ async function ensureBackupToolchain(connection, options) {
204
+ const serverMajor = Number(connection.serverMajor);
205
+ if (!Number.isInteger(serverMajor) || serverMajor <= 0) {
206
+ return { action: 'setup.backup-toolchain', status: 'skipped', reason: 'server_major_unknown' };
207
+ }
208
+ const binDir = explicitPgBinDir(options.postgresBinDir);
209
+ const probe = (command, args) => {
210
+ const result = spawnSync(command, args, { encoding: 'utf8' });
211
+ if (result.error || result.status !== 0)
212
+ throw new Error('pg_client_probe_failed');
213
+ return `${result.stdout ?? ''}`;
214
+ };
215
+ const before = inspectPgToolchain({ serverMajor, run: probe, ...(binDir ? { binDir } : {}) });
216
+ if (before.ready) {
217
+ return { action: 'setup.backup-toolchain', status: 'ready', serverMajor, source: binDir ? 'operator' : 'host' };
218
+ }
219
+ const outcome = acquirePgClient({
220
+ major: serverMajor,
221
+ run: (command, args) => {
222
+ const result = spawnSync(command, [...args], { encoding: 'utf8' });
223
+ if (result.error)
224
+ throw result.error;
225
+ if (result.status !== 0)
226
+ throw new Error(`${command} exited with ${result.status ?? 'unknown'}`);
227
+ },
228
+ });
229
+ if (outcome.status !== 'acquired') {
230
+ return {
231
+ action: 'setup.backup-toolchain',
232
+ status: 'action-required',
233
+ serverMajor,
234
+ cause: before.cause,
235
+ ...(outcome.status === 'failed' ? { install: outcome.plan.display, reason: outcome.reason } : {}),
236
+ next: 'Install PostgreSQL client tools of this major, or pass --postgres-bin-dir if they are already installed.',
237
+ };
238
+ }
239
+ const after = inspectPgToolchain({ serverMajor, run: probe, ...(binDir ? { binDir } : {}) });
240
+ return {
241
+ action: 'setup.backup-toolchain',
242
+ status: after.ready ? 'installed' : 'action-required',
243
+ serverMajor,
244
+ installed: outcome.plan.display,
245
+ ...(after.ready ? {} : { cause: after.cause }),
246
+ };
247
+ }
200
248
  export async function runSetupInitOrchestrator(options = {}, dependencies = {}) {
201
249
  const inspectReadiness = dependencies.inspectReadiness ?? inspectSetupReadiness;
202
250
  const setupCommunityServer = dependencies.setupCommunityServer ?? runCommunityServerSetup;
@@ -408,6 +456,7 @@ export async function runSetupInitOrchestrator(options = {}, dependencies = {})
408
456
  throw error;
409
457
  }
410
458
  steps.push({ action: 'setup.postgres-connection', ...connection });
459
+ steps.push(await ensureBackupToolchain(connection, options));
411
460
  }
412
461
  if (selectedPath === 'native-local') {
413
462
  const localCheck = initial.checks.find((check) => check.id === 'local-postgresql');
@@ -42,7 +42,7 @@
42
42
  "capabilityClass": "windows-ntfs-crash-recoverable-v1",
43
43
  "qualification": {
44
44
  "relativePath": "qualifications/win32-x64.json",
45
- "sha256": "25c8d0971355c6d4f73817dd1b1a91d40579313a43a1624c35427f3445af942a"
45
+ "sha256": "8cc3d15f86ed5b243b4d2ba834f239e57db89c0a9987e5d7ca30ab13cc6b2706"
46
46
  }
47
47
  }
48
48
  ]
@@ -7,7 +7,7 @@
7
7
  "filesystem": "NTFS",
8
8
  "primitiveProfile": "windows-createfile-flush-movefileex-v1",
9
9
  "capabilityClass": "windows-ntfs-crash-recoverable-v1",
10
- "evidenceSha256": "2090a30f52f1d5f2e4c2fd1dfcc0095966e998224ce023cc9fbeb456dc92617a",
10
+ "evidenceSha256": "8d22f4416cad09beaa5f8b81b01b3da95cf9f2dca5d270f6f25ce7b67b8dd778",
11
11
  "crashInjectionPoints": [
12
12
  "authority-history",
13
13
  "authority-current",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aopslabs/aops",
3
- "version": "0.3.26",
3
+ "version": "0.3.28",
4
4
  "type": "module",
5
5
  "description": "AOPS CLI and terminal setup application.",
6
6
  "aopsDockerServerVersion": "0.2.17",
@@ -23,7 +23,7 @@
23
23
  "test:setup-docker": "node --test test/setup-docker-server.test.mjs",
24
24
  "test:native-lifecycle": "node --test test/community-native-lifecycle.test.mjs",
25
25
  "test:migration-snapshot-gate": "node --test test/community-migration-snapshot-gate.test.mjs",
26
- "test:backup-gate-parity": "node --test test/community-backup-gate-parity.test.mjs test/community-backup-list.test.mjs test/community-native-backup.test.mjs",
26
+ "test:backup-gate-parity": "node --test test/community-backup-gate-parity.test.mjs test/community-backup-list.test.mjs test/community-native-backup.test.mjs test/pg-client-acquisition.test.mjs",
27
27
  "test:migration-auto-dump": "node --test test/community-migration-auto-dump.test.mjs",
28
28
  "test:memory-search-window": "node --test test/memory-search-window-shape.test.mjs",
29
29
  "test:db-restore-source-root": "node --test test/db-restore-source-root.test.mjs",
@@ -70,15 +70,16 @@
70
70
  "test:chat-wake-watch": "node --test test/chat-wake-watch.test.mjs",
71
71
  "test:codex-wake": "node --test test/codex-wake.test.mjs",
72
72
  "test:commercial-lifecycle-evidence": "node --test test/commercial-lifecycle-evidence.test.mjs test/commercial-lifecycle-evidence-projection.test.mjs",
73
- "test:commercial-effects": "node --test test/commercial-interruption-removal-effects.test.mjs test/commercial-natural-controls-effects.test.mjs test/commercial-transition-effects.test.mjs"
73
+ "test:commercial-effects": "node --test test/commercial-interruption-removal-effects.test.mjs test/commercial-natural-controls-effects.test.mjs test/commercial-transition-effects.test.mjs",
74
+ "test:pg-client-acquisition": "node --test test/pg-client-acquisition.test.mjs"
74
75
  },
75
76
  "dependencies": {
76
77
  "@aopslabs/aops-agent-runtime": "0.1.0",
77
- "@aopslabs/aops-server": "0.2.20",
78
+ "@aopslabs/aops-server": "0.2.22",
78
79
  "@aopslabs/artifact-trust-contracts": "0.3.1",
79
80
  "@aopslabs/cli-kit": "0.1.0",
80
81
  "@aopslabs/aops-host-registration": "0.2.2",
81
- "@aopslabs/aops-pg-bootstrap": "0.2.5",
82
+ "@aopslabs/aops-pg-bootstrap": "0.2.7",
82
83
  "@aopslabs/aops-runtime-config": "0.2.2",
83
84
  "@sigstore/bundle": "4.0.0",
84
85
  "@sigstore/protobuf-specs": "0.5.1",