@aopslabs/aops 0.3.23 → 0.3.24

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.
@@ -117,6 +117,6 @@
117
117
  },
118
118
  "schemaVersion": 1,
119
119
  "standard": "aops-community-core-v1",
120
- "version": "0.3.23",
121
- "versionId": "community-core-0.3.23"
120
+ "version": "0.3.24",
121
+ "versionId": "community-core-0.3.24"
122
122
  }
@@ -10,8 +10,8 @@
10
10
  "name": "aops",
11
11
  "optional": false,
12
12
  "packageSha256": "647c6e84fbc48f9a36945d81660f4d2a5241f58a53a18054365bfc16868b0f09",
13
- "version": "0.3.23",
14
- "versionId": "community-core-0.3.23"
13
+ "version": "0.3.24",
14
+ "versionId": "community-core-0.3.24"
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": "ca16ea2f40b73185a126494df49ad4883c091fd5",
25
+ "commit": "608842f09f9479d542bac1bb6aa5034dd0002fd4",
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": "647c6e84fbc48f9a36945d81660f4d2a5241f58a53a18054365bfc16868b0f09",
14
- "version": "0.3.23",
15
- "versionId": "community-core-0.3.23"
14
+ "version": "0.3.24",
15
+ "versionId": "community-core-0.3.24"
16
16
  },
17
17
  "files": [
18
18
  {
@@ -123,7 +123,7 @@
123
123
  {
124
124
  "byteLength": 3978,
125
125
  "ref": "agent-assets/core/647c6e84fbc48f9a36945d81660f4d2a5241f58a53a18054365bfc16868b0f09/manifest.json",
126
- "sha256": "c8131d55a080c16afdb08d6203b8a63251fbaad3fde7411ca0c52db3579030cb"
126
+ "sha256": "ac7e50f6d7ee959d309784edadeed6138980307f47c895cc79f3684bb91ea566"
127
127
  },
128
128
  {
129
129
  "byteLength": 621,
@@ -133,7 +133,7 @@
133
133
  {
134
134
  "byteLength": 926,
135
135
  "ref": "agent-assets/inventory.json",
136
- "sha256": "c16355c67f2ca0a0b9cd7c5dc0e9fe0f0292dea4d7ab4430bf49f8cad26e644c"
136
+ "sha256": "eabd4cb45defd761d6c91b5383f80351a59162d49fa3996a47e134e7965fb2ab"
137
137
  },
138
138
  {
139
139
  "byteLength": 281,
@@ -149,7 +149,7 @@
149
149
  },
150
150
  "inventory": {
151
151
  "ref": "agent-assets/inventory.json",
152
- "sha256": "c16355c67f2ca0a0b9cd7c5dc0e9fe0f0292dea4d7ab4430bf49f8cad26e644c"
152
+ "sha256": "eabd4cb45defd761d6c91b5383f80351a59162d49fa3996a47e134e7965fb2ab"
153
153
  },
154
154
  "kind": "aops-community-agent-assets-release-v1",
155
155
  "projection": {
@@ -158,7 +158,7 @@
158
158
  },
159
159
  "root": "agent-assets",
160
160
  "schemaVersion": 1,
161
- "setSha256": "d603ef7fb337dd0b9b69f7b955989ec6ec80a7c4cb98f0b27183010d06146f29"
161
+ "setSha256": "554203cf8d06ba4ca792e79a5a616b3ceaaa3c50b840e65745d87433cc43ce95"
162
162
  },
163
163
  "evidence": {
164
164
  "signature": {
@@ -166,10 +166,10 @@
166
166
  }
167
167
  },
168
168
  "kind": "aops-community-agent-assets-bundle-v1",
169
- "releaseVersion": "0.3.23",
169
+ "releaseVersion": "0.3.24",
170
170
  "schemaVersion": 1,
171
171
  "source": {
172
- "commit": "ca16ea2f40b73185a126494df49ad4883c091fd5",
172
+ "commit": "608842f09f9479d542bac1bb6aa5034dd0002fd4",
173
173
  "repository": "git+https://github.com/eeemzs/aops-platform",
174
174
  "root": "apps/aops-cli/assets/agent-assets"
175
175
  }
@@ -4,13 +4,13 @@
4
4
  "x509CertificateChain": {
5
5
  "certificates": [
6
6
  {
7
- "rawBytes": "MIIHdjCCBvugAwIBAgIUMXLLfxzYudZHfH1VdMyPOkizPL0wCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjYwODE3MDgyNDE5WhcNMjYwODE3MDgzNDE5WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEOxJ2mg4zXjxmt2NPD5mLKal6o+3ishpZtlm1R6VeFd2SKVcEe+cNcFu4WsL96qbqCQ+EKwaBQy5gNjBrnJ8aqKOCBhowggYWMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUM+Vc4BC0wVz5IL3kUTso1HKDyxQwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wdAYDVR0RAQH/BGowaIZmaHR0cHM6Ly9naXRodWIuY29tL2VlZW16cy9hb3BzLXBsYXRmb3JtLy5naXRodWIvd29ya2Zsb3dzL3NpZ24tcHVibGljLWFnZW50LWFzc2V0cy55bWxAcmVmcy9oZWFkcy9tYWluMDkGCisGAQQBg78wAQEEK2h0dHBzOi8vdG9rZW4uYWN0aW9ucy5naXRodWJ1c2VyY29udGVudC5jb20wHwYKKwYBBAGDvzABAgQRd29ya2Zsb3dfZGlzcGF0Y2gwNgYKKwYBBAGDvzABAwQoY2ExNmVhMmY0MGI3MzE4NWExMjY0OTRkZjQ5YWQ0ODgzYzA5MWZkNTAxBgorBgEEAYO/MAEEBCNTaWduIGV4YWN0IHB1YmxpYyBBT1BTIGFnZW50IGFzc2V0czAiBgorBgEEAYO/MAEFBBRlZWVtenMvYW9wcy1wbGF0Zm9ybTAdBgorBgEEAYO/MAEGBA9yZWZzL2hlYWRzL21haW4wOwYKKwYBBAGDvzABCAQtDCtodHRwczovL3Rva2VuLmFjdGlvbnMuZ2l0aHVidXNlcmNvbnRlbnQuY29tMHYGCisGAQQBg78wAQkEaAxmaHR0cHM6Ly9naXRodWIuY29tL2VlZW16cy9hb3BzLXBsYXRmb3JtLy5naXRodWIvd29ya2Zsb3dzL3NpZ24tcHVibGljLWFnZW50LWFzc2V0cy55bWxAcmVmcy9oZWFkcy9tYWluMDgGCisGAQQBg78wAQoEKgwoY2ExNmVhMmY0MGI3MzE4NWExMjY0OTRkZjQ5YWQ0ODgzYzA5MWZkNTAdBgorBgEEAYO/MAELBA8MDWdpdGh1Yi1ob3N0ZWQwNwYKKwYBBAGDvzABDAQpDCdodHRwczovL2dpdGh1Yi5jb20vZWVlbXpzL2FvcHMtcGxhdGZvcm0wOAYKKwYBBAGDvzABDQQqDChjYTE2ZWEyZjQwYjczMTg1YTEyNjQ5NGRmNDlhZDQ4ODNjMDkxZmQ1MB8GCisGAQQBg78wAQ4EEQwPcmVmcy9oZWFkcy9tYWluMBoGCisGAQQBg78wAQ8EDAwKMTMxNjI1NTAyNjApBgorBgEEAYO/MAEQBBsMGWh0dHBzOi8vZ2l0aHViLmNvbS9lZWVtenMwGQYKKwYBBAGDvzABEQQLDAkxMDkzMTA3NzQwdgYKKwYBBAGDvzABEgRoDGZodHRwczovL2dpdGh1Yi5jb20vZWVlbXpzL2FvcHMtcGxhdGZvcm0vLmdpdGh1Yi93b3JrZmxvd3Mvc2lnbi1wdWJsaWMtYWdlbnQtYXNzZXRzLnltbEByZWZzL2hlYWRzL21haW4wOAYKKwYBBAGDvzABEwQqDChjYTE2ZWEyZjQwYjczMTg1YTEyNjQ5NGRmNDlhZDQ4ODNjMDkxZmQ1MCEGCisGAQQBg78wARQEEwwRd29ya2Zsb3dfZGlzcGF0Y2gwWwYKKwYBBAGDvzABFQRNDEtodHRwczovL2dpdGh1Yi5jb20vZWVlbXpzL2FvcHMtcGxhdGZvcm0vYWN0aW9ucy9ydW5zLzMyMDEwMTMxMDI2L2F0dGVtcHRzLzEwFwYKKwYBBAGDvzABFgQJDAdwcml2YXRlMFIGCisGAQQBg78wARgERAxCcmVwbzplZWVtenNAMTA5MzEwNzc0L2FvcHMtcGxhdGZvcm1AMTMxNjI1NTAyNjpyZWY6cmVmcy9oZWFkcy9tYWluMIGLBgorBgEEAdZ5AgQCBH0EewB5AHcA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGgDtJSCAAABAMASDBGAiEAu9ycW/kwrZjltGqQfXkOI71m8LVciG5GsHOorD9xk8ECIQDUbBnqIWEPvkUvMc09WQwurwIJWPQ7cPMuPMSwo0QYETAKBggqhkjOPQQDAwNpADBmAjEAhiIU3mJJ5fRa21i4tyVfgPhoxtJpJ2gW3rFgyebKZhQkeg2ZUciI9uB4E/sBMPvpAjEAskHXKRuh9RG41rtxrMgaFTwvRR4p4Cldi5fFNJboD5IJxEQI7cK5prsuL5oJfeeS"
7
+ "rawBytes": "MIIHdDCCBvqgAwIBAgIUI4fR18PdiRxO8FaqGMzaq47vZBQwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjYwODE4MTA1NTA3WhcNMjYwODE4MTEwNTA3WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgwPz67a3lrea2cpXz2d3l8wgQaGkb9mhbaDRI5KIbM+ITq24pVCvlyTjP7Q7cX3ShGPLUlbl2QApCzJzLP3WeKOCBhkwggYVMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUM0HWpHOJwVhN2Vvr/7dm2JTUy78wHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wdAYDVR0RAQH/BGowaIZmaHR0cHM6Ly9naXRodWIuY29tL2VlZW16cy9hb3BzLXBsYXRmb3JtLy5naXRodWIvd29ya2Zsb3dzL3NpZ24tcHVibGljLWFnZW50LWFzc2V0cy55bWxAcmVmcy9oZWFkcy9tYWluMDkGCisGAQQBg78wAQEEK2h0dHBzOi8vdG9rZW4uYWN0aW9ucy5naXRodWJ1c2VyY29udGVudC5jb20wHwYKKwYBBAGDvzABAgQRd29ya2Zsb3dfZGlzcGF0Y2gwNgYKKwYBBAGDvzABAwQoNjA4ODQyZjA5Zjk0NzlkNTQyYmFjMWJiNmFhNTAzNGRkMDAwMmZkNDAxBgorBgEEAYO/MAEEBCNTaWduIGV4YWN0IHB1YmxpYyBBT1BTIGFnZW50IGFzc2V0czAiBgorBgEEAYO/MAEFBBRlZWVtenMvYW9wcy1wbGF0Zm9ybTAdBgorBgEEAYO/MAEGBA9yZWZzL2hlYWRzL21haW4wOwYKKwYBBAGDvzABCAQtDCtodHRwczovL3Rva2VuLmFjdGlvbnMuZ2l0aHVidXNlcmNvbnRlbnQuY29tMHYGCisGAQQBg78wAQkEaAxmaHR0cHM6Ly9naXRodWIuY29tL2VlZW16cy9hb3BzLXBsYXRmb3JtLy5naXRodWIvd29ya2Zsb3dzL3NpZ24tcHVibGljLWFnZW50LWFzc2V0cy55bWxAcmVmcy9oZWFkcy9tYWluMDgGCisGAQQBg78wAQoEKgwoNjA4ODQyZjA5Zjk0NzlkNTQyYmFjMWJiNmFhNTAzNGRkMDAwMmZkNDAdBgorBgEEAYO/MAELBA8MDWdpdGh1Yi1ob3N0ZWQwNwYKKwYBBAGDvzABDAQpDCdodHRwczovL2dpdGh1Yi5jb20vZWVlbXpzL2FvcHMtcGxhdGZvcm0wOAYKKwYBBAGDvzABDQQqDCg2MDg4NDJmMDlmOTQ3OWQ1NDJiYWMxYmI2YWE1MDM0ZGQwMDAyZmQ0MB8GCisGAQQBg78wAQ4EEQwPcmVmcy9oZWFkcy9tYWluMBoGCisGAQQBg78wAQ8EDAwKMTMxNjI1NTAyNjApBgorBgEEAYO/MAEQBBsMGWh0dHBzOi8vZ2l0aHViLmNvbS9lZWVtenMwGQYKKwYBBAGDvzABEQQLDAkxMDkzMTA3NzQwdgYKKwYBBAGDvzABEgRoDGZodHRwczovL2dpdGh1Yi5jb20vZWVlbXpzL2FvcHMtcGxhdGZvcm0vLmdpdGh1Yi93b3JrZmxvd3Mvc2lnbi1wdWJsaWMtYWdlbnQtYXNzZXRzLnltbEByZWZzL2hlYWRzL21haW4wOAYKKwYBBAGDvzABEwQqDCg2MDg4NDJmMDlmOTQ3OWQ1NDJiYWMxYmI2YWE1MDM0ZGQwMDAyZmQ0MCEGCisGAQQBg78wARQEEwwRd29ya2Zsb3dfZGlzcGF0Y2gwWwYKKwYBBAGDvzABFQRNDEtodHRwczovL2dpdGh1Yi5jb20vZWVlbXpzL2FvcHMtcGxhdGZvcm0vYWN0aW9ucy9ydW5zLzMyMTI5MDQ1NDA2L2F0dGVtcHRzLzEwFwYKKwYBBAGDvzABFgQJDAdwcml2YXRlMFIGCisGAQQBg78wARgERAxCcmVwbzplZWVtenNAMTA5MzEwNzc0L2FvcHMtcGxhdGZvcm1AMTMxNjI1NTAyNjpyZWY6cmVmcy9oZWFkcy9tYWluMIGKBgorBgEEAdZ5AgQCBHwEegB4AHYA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGgFIK+CAAABAMARzBFAiEAy9KcY7oLSBFe8gWmCXHajwk34lrGKmSYi7+Z0wYZwP4CIELankUfWbto0ga+4wM8apIo6G7j+qNYel/Lh4PrfPlmMAoGCCqGSM49BAMDA2gAMGUCMQCbIEu/LsDzUcvQJoj+vG2wt04hn5MFNlUjiC3RSbzBRUVWB69Bopz6dOpfcGySBp8CMH/sNkjPVONkqeoFQyMN+z1dr6JtKtuhBad0EYB0gXeblEVKxRwZqDqJGugYtv7rpg=="
8
8
  }
9
9
  ]
10
10
  },
11
11
  "tlogEntries": [
12
12
  {
13
- "logIndex": "2496923676",
13
+ "logIndex": "2501603598",
14
14
  "logId": {
15
15
  "keyId": "wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="
16
16
  },
@@ -18,31 +18,28 @@
18
18
  "kind": "hashedrekord",
19
19
  "version": "0.0.1"
20
20
  },
21
- "integratedTime": "1786955059",
21
+ "integratedTime": "1787050507",
22
22
  "inclusionPromise": {
23
- "signedEntryTimestamp": "MEQCIAwYgvet0QhdU+0sab339djURwhc02dqUeczR3Djxeh6AiAYzJi/wt5yn4jH6fMxUNy6yEibNhUcI1Utlv/YT3pxTQ=="
23
+ "signedEntryTimestamp": "MEQCIBmNzj9XxKBpaqvsYHJdQuuBJmC3CFTb1ymB/wT4v2FrAiB+PXmrkgK7nivM0W5CFuPrGdQanm94uTZlSYKfkantQA=="
24
24
  },
25
25
  "inclusionProof": {
26
- "logIndex": "2375019414",
27
- "rootHash": "1IhSyO9LuJsXp4+LouxnhVNk1qxoEN5K6R+qlT5JcAw=",
28
- "treeSize": "2375019420",
26
+ "logIndex": "2379699336",
27
+ "rootHash": "YBqB3LPP7z3XiaRq2ot4MJFVi+bFDVkUMph/hC1IDfU=",
28
+ "treeSize": "2379699344",
29
29
  "hashes": [
30
- "1lcDoWCuU0ZryC48fV92JsWXiKUrGP6K+N0l1S2cwic=",
31
- "6KU0t9wzQ9UTCRK4LjUzN/Cn2ppRpEIA/VgSSSXsOa4=",
32
- "uNcm+KnEMiXph9+jKRwgdEy10pxYy3OCZCywcXWm+sY=",
33
- "QbsTY+/VuDNOabZRMUBLWZ05IBVJBiPYAgPj6iCXXpo=",
34
- "DGdyZuKSWMLmOqAq+rDuvDzFZGyQTg0FAr9uvQLw3/c=",
35
- "PvgmuENb2EpHATFzVEb76dlVMxZbkulfujl8lX9OuXk=",
36
- "CioIeHI5xrZZmT44FUv/Q8SArLvHBnR0gsvIV7rAE+M=",
37
- "zKkaTQOlbK1wYyjYfIrAK6O94yREtvgSj8+whe+Gp1Q=",
38
- "/zBUG2Mf+Idzcv4QJncssZ1EK2C9oTWoWcIrBlnTSDc=",
39
- "H7rj+52anAGwsH2w7iJw9RikbV9kCMiHVjWb9g8EtVw=",
40
- "DIZ1PKLUjpkt6Pzn4ex1eYRKdMPYOdlax0lMduqv+bA=",
41
- "M37U32DNWM5gQ+tJGFZkumfFxm+SWzWZIyxGrkd+ecA=",
42
- "YPztLJ/GLmbbWAUdMzJlIzYK0VbVVauzuvV9Q4L7HPk=",
43
- "3Q5lN6yq8podpJsAKbBJqSGNs+rmiL9QwZbQ0UH+3zQ=",
44
- "fA/Fxwaq7dI1l5khmfZO/yXBJ/beTLDtlbUbvr3BnMI=",
45
- "D2gSVC/Ew66euzf7kcNDzk5ThToLAkj5W34g+R2yIAQ=",
30
+ "njwwRR8CkW8qKsjNsTk2DmWb1d0QYVnDjoaFjnNiZcM=",
31
+ "VqADwLGk4e4wYy+SINhpx8di8xs/yXzC+8hp57515DQ=",
32
+ "KNNPpwh23nPcjbq8p5KBXjTXlnGR4xCBaYpvJypOGIM=",
33
+ "WeDaME+8oVrLbuTd5pesPBQyNEAJI32luWOTYeZlAvg=",
34
+ "lVWszKQLwZ+PwxkyaYfq05JuVEF7HtUP2bXechNYO1w=",
35
+ "PMq4/z5Dji4lnm1dN9aik+bf21gQHuaV0DvEF2DRfK4=",
36
+ "jw/BXoahnA99/KDbOEXR342svCgXlI8fBQ1EdfoP0rY=",
37
+ "V2ZRaYqGvqopvORHedITMz+sBSInRp6SKdmpIyoE4c0=",
38
+ "2ccjkNTG2bujC4xzJzdJCckwqIh5Q4aSWapj9M4EG/Y=",
39
+ "TyajcYUKvFL3MiVt2E0QMAVavx7MDeqclu7zuKIHKWA=",
40
+ "hwAkEuPLcomSOf+KiIMjdoM4HdEqmumZzGAuuolVGdk=",
41
+ "lVKLLzp8tNcT7yWZhGMjc/Cer1kKYS71H/n0sNutLzk=",
42
+ "1dSZjpCCNpV0knAvzBTUqXzpyGFhCaJkg0vBf6yDS/s=",
46
43
  "9PAe2QaxB0ZutC/Y+HQKC6Wnag9Df+yGa0xYuRz5f1w=",
47
44
  "dGyfnQgNT9mPiDmeuOaMmiIIn2XY7kncEgQ/mDW3SJU=",
48
45
  "1OEC0KMekWr1AbpIDYXYCOXzf6aK8hYAK01U3ypm0is=",
@@ -50,10 +47,10 @@
50
47
  "xH/DCseLHr9eKoYT8qsORZK7zVdEGYWHuVtsVrD95wY="
51
48
  ],
52
49
  "checkpoint": {
53
- "envelope": "rekor.sigstore.dev - 1193050959916656506\n2375019420\n1IhSyO9LuJsXp4+LouxnhVNk1qxoEN5K6R+qlT5JcAw=\n\n— rekor.sigstore.dev wNI9ajBFAiA/3xFOdX6QXZNZjx3kpJ1y2m7/Gmg1PFYxxFZYff3nfgIhAPM8qWzmZyTzffF5m2aClQ6Szz3wyHoMwommTTu5yUFr\n"
50
+ "envelope": "rekor.sigstore.dev - 1193050959916656506\n2379699344\nYBqB3LPP7z3XiaRq2ot4MJFVi+bFDVkUMph/hC1IDfU=\n\n— rekor.sigstore.dev wNI9ajBGAiEA74PW9f4HTjAKRYHz4Up0pM3rkbma8803/TlH/8V0IF0CIQC2DmHPhZ7X0Jf8QQNood1Ud3VsmTmJpY7aXZYQ2el1/A==\n"
54
51
  }
55
52
  },
56
- "canonicalizedBody": "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiIzOTNkZmI0MGU0ODI4OGRjNDQ2NTBhMTRjNDkwZDQ5NzMwNDMyZGRiY2MyZDgzM2U5NGIyNzllMzJhYjgyNTEzIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FVUNJSHMya3hmQ3RvQ1I4cjg1emttR00wLzlvNGdGQzZhYUNFOGtDbjhvWUl2V0FpRUFobUpiUzlOSG9IeHpXYmJzNTRReTl0ckFVTDNBUmJ0NmZnV2lzdlJNc1drPSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVaGtha05EUW5aMVowRjNTVUpCWjBsVlRWaE1UR1o0ZWxsMVpGcElaa2d4Vm1STmVWQlBhMmw2VUV3d2QwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcFpkMDlFUlROTlJHZDVUa1JGTlZkb1kwNU5hbGwzVDBSRk0wMUVaM3BPUkVVMVYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVZQZUVveWJXYzBlbGhxZUcxME1rNVFSRFZ0VEV0aGJEWnZLek5wYzJod1duUnNiVEVLVWpaV1pVWmtNbE5MVm1ORlpTdGpUbU5HZFRSWGMwdzVObkZpY1VOUkswVkxkMkZDVVhrMVowNXFRbkp1U2poaGNVdFBRMEpvYjNkbloxbFhUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlZOSzFaakNqUkNRekIzVm5vMVNVd3phMVZVYzI4eFNFdEVlWGhSZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDJSQldVUldVakJTUVZGSUwwSkhiM2RoU1ZwdFlVaFNNR05JVFRaTWVUbHVZVmhTYjJSWFNYVlpNamwwVERKV2JGcFhNVFpqZVRsb1lqTkNlZ3BNV0VKeldWaFNiV0l6U25STWVUVnVZVmhTYjJSWFNYWmtNamw1WVRKYWMySXpaSHBNTTA1d1dqSTBkR05JVm1saVIyeHFURmRHYmxwWE5UQk1WMFo2Q21NeVZqQmplVFUxWWxkNFFXTnRWbTFqZVRsdldsZEdhMk41T1hSWlYyeDFUVVJyUjBOcGMwZEJVVkZDWnpjNGQwRlJSVVZMTW1nd1pFaENlazlwT0hZS1pFYzVjbHBYTkhWWlYwNHdZVmM1ZFdONU5XNWhXRkp2WkZkS01XTXlWbmxaTWpsMVpFZFdkV1JETldwaU1qQjNTSGRaUzB0M1dVSkNRVWRFZG5wQlFncEJaMUZTWkRJNWVXRXlXbk5pTTJSbVdrZHNlbU5IUmpCWk1tZDNUbWRaUzB0M1dVSkNRVWRFZG5wQlFrRjNVVzlaTWtWNFRtMVdhRTF0V1RCTlIwa3pDazE2UlRST1YwVjRUV3BaTUU5VVVtdGFhbEUxV1ZkUk1FOUVaM3BaZWtFMVRWZGFhMDVVUVhoQ1oyOXlRbWRGUlVGWlR5OU5RVVZGUWtOT1ZHRlhaSFVLU1VkV05GbFhUakJKU0VJeFdXMTRjRmw1UWtKVU1VSlVTVWRHYmxwWE5UQkpSMFo2WXpKV01HTjZRV2xDWjI5eVFtZEZSVUZaVHk5TlFVVkdRa0pTYkFwYVYxWjBaVzVOZGxsWE9YZGplVEYzWWtkR01GcHRPWGxpVkVGa1FtZHZja0puUlVWQldVOHZUVUZGUjBKQk9YbGFWMXA2VERKb2JGbFhVbnBNTWpGb0NtRlhOSGRQZDFsTFMzZFpRa0pCUjBSMmVrRkNRMEZSZEVSRGRHOWtTRkozWTNwdmRrd3pVblpoTWxaMVRHMUdhbVJIYkhaaWJrMTFXakpzTUdGSVZta0taRmhPYkdOdFRuWmlibEpzWW01UmRWa3lPWFJOU0ZsSFEybHpSMEZSVVVKbk56aDNRVkZyUldGQmVHMWhTRkl3WTBoTk5reDVPVzVoV0ZKdlpGZEpkUXBaTWpsMFRESldiRnBYTVRaamVUbG9Zak5DZWt4WVFuTlpXRkp0WWpOS2RFeDVOVzVoV0ZKdlpGZEpkbVF5T1hsaE1scHpZak5rZWt3elRuQmFNalIwQ21OSVZtbGlSMnhxVEZkR2JscFhOVEJNVjBaNll6SldNR041TlRWaVYzaEJZMjFXYldONU9XOWFWMFpyWTNrNWRGbFhiSFZOUkdkSFEybHpSMEZSVVVJS1p6YzRkMEZSYjBWTFozZHZXVEpGZUU1dFZtaE5iVmt3VFVkSk0wMTZSVFJPVjBWNFRXcFpNRTlVVW10YWFsRTFXVmRSTUU5RVozcFpla0UxVFZkYWF3cE9WRUZrUW1kdmNrSm5SVVZCV1U4dlRVRkZURUpCT0UxRVYyUndaRWRvTVZscE1XOWlNMDR3V2xkUmQwNTNXVXRMZDFsQ1FrRkhSSFo2UVVKRVFWRndDa1JEWkc5a1NGSjNZM3B2ZGt3eVpIQmtSMmd4V1drMWFtSXlNSFphVjFac1lsaHdla3d5Um5aalNFMTBZMGQ0YUdSSFduWmpiVEIzVDBGWlMwdDNXVUlLUWtGSFJIWjZRVUpFVVZGeFJFTm9hbGxVUlRKYVYwVjVXbXBSZDFscVkzcE5WR2N4V1ZSRmVVNXFVVFZPUjFKdFRrUnNhRnBFVVRSUFJFNXFUVVJyZUFwYWJWRXhUVUk0UjBOcGMwZEJVVkZDWnpjNGQwRlJORVZGVVhkUVkyMVdiV041T1c5YVYwWnJZM2s1ZEZsWGJIVk5RbTlIUTJselIwRlJVVUpuTnpoM0NrRlJPRVZFUVhkTFRWUk5lRTVxU1RGT1ZFRjVUbXBCY0VKbmIzSkNaMFZGUVZsUEwwMUJSVkZDUW5OTlIxZG9NR1JJUW5wUGFUaDJXakpzTUdGSVZta0tURzFPZG1KVE9XeGFWMVowWlc1TmQwZFJXVXRMZDFsQ1FrRkhSSFo2UVVKRlVWRk1SRUZyZUUxRWEzcE5WRUV6VG5wUmQyUm5XVXRMZDFsQ1FrRkhSQXAyZWtGQ1JXZFNiMFJIV205a1NGSjNZM3B2ZGt3eVpIQmtSMmd4V1drMWFtSXlNSFphVjFac1lsaHdla3d5Um5aalNFMTBZMGQ0YUdSSFduWmpiVEIyQ2t4dFpIQmtSMmd4V1drNU0ySXpTbkphYlhoMlpETk5kbU15Ykc1aWFURjNaRmRLYzJGWFRYUlpWMlJzWW01UmRGbFlUbnBhV0ZKNlRHNXNkR0pGUW5rS1dsZGFla3d5YUd4WlYxSjZUREl4YUdGWE5IZFBRVmxMUzNkWlFrSkJSMFIyZWtGQ1JYZFJjVVJEYUdwWlZFVXlXbGRGZVZwcVVYZFphbU42VFZSbk1RcFpWRVY1VG1wUk5VNUhVbTFPUkd4b1drUlJORTlFVG1wTlJHdDRXbTFSTVUxRFJVZERhWE5IUVZGUlFtYzNPSGRCVWxGRlJYZDNVbVF5T1hsaE1scHpDbUl6WkdaYVIyeDZZMGRHTUZreVozZFhkMWxMUzNkWlFrSkJSMFIyZWtGQ1JsRlNUa1JGZEc5a1NGSjNZM3B2ZGt3eVpIQmtSMmd4V1drMWFtSXlNSFlLV2xkV2JHSlljSHBNTWtaMlkwaE5kR05IZUdoa1IxcDJZMjB3ZGxsWFRqQmhWemwxWTNrNWVXUlhOWHBNZWsxNVRVUkZkMDFVVFhoTlJFa3lUREpHTUFwa1IxWjBZMGhTZWt4NlJYZEdkMWxMUzNkWlFrSkJSMFIyZWtGQ1JtZFJTa1JCWkhkamJXd3lXVmhTYkUxR1NVZERhWE5IUVZGUlFtYzNPSGRCVW1kRkNsSkJlRU5qYlZaM1lucHdiRnBYVm5SbGJrNUJUVlJCTlUxNlJYZE9lbU13VERKR2RtTklUWFJqUjNob1pFZGFkbU50TVVGTlZFMTRUbXBKTVU1VVFYa0tUbXB3ZVZwWFdUWmpiVlp0WTNrNWIxcFhSbXRqZVRsMFdWZHNkVTFKUjB4Q1oyOXlRbWRGUlVGa1dqVkJaMUZEUWtnd1JXVjNRalZCU0dOQk0xUXdkd3BoYzJKSVJWUktha2RTTkdOdFYyTXpRWEZLUzFoeWFtVlFTek12YURSd2VXZERPSEEzYnpSQlFVRkhaMFIwU2xORFFVRkJRa0ZOUVZORVFrZEJhVVZCQ25VNWVXTlhMMnQzY2xwcWJIUkhjVkZtV0d0UFNUY3hiVGhNVm1OcFJ6VkhjMGhQYjNKRU9YaHJPRVZEU1ZGRVZXSkNibkZKVjBWUWRtdFZkazFqTURrS1YxRjNkWEozU1VwWFVGRTNZMUJOZFZCTlUzZHZNRkZaUlZSQlMwSm5aM0ZvYTJwUFVGRlJSRUYzVG5CQlJFSnRRV3BGUVdocFNWVXpiVXBLTldaU1lRb3lNV2swZEhsV1ptZFFhRzk0ZEVwd1NqSm5Wek55Um1kNVpXSkxXbWhSYTJWbk1scFZZMmxKT1hWQ05FVXZjMEpOVUhad1FXcEZRWE5yU0ZoTFVuVm9DamxTUnpReGNuUjRjazFuWVVaVWQzWlNValJ3TkVOc1pHazFaa1pPU21KdlJEVkpTbmhGVVVrM1kwczFjSEp6ZFV3MWIwcG1aV1ZUQ2kwdExTMHRSVTVFSUVORlVsUkpSa2xEUVZSRkxTMHRMUzBLIn19fX0="
53
+ "canonicalizedBody": "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiIzY2Q0YzYzYjI3YzdhYWI1NGVmMGFjZTllYmNkNTQ5YzgyNjYyZTc1ZDUzNmVjNjRjMjcyNzNmZDRhMDUwZTM1In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FVUNJUUNpb2d4R01HMkkrZ1ZudnBNanhUa0I4cTR2cEc3TFFPQjJEdVJVb3YzQmV3SWdQZG15WkpQandKZnJscDdRazM0aTltN3hYWUV5SU5PSzZGbmp0VzF0aDQ4PSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVaGtSRU5EUW5aeFowRjNTVUpCWjBsVlNUUm1VakU0VUdScFVuaFBPRVpoY1VkTmVtRnhORGQyV2tKUmQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcFpkMDlFUlRSTlZFRXhUbFJCTTFkb1kwNU5hbGwzVDBSRk5FMVVSWGRPVkVFelYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVZuZDFCNk5qZGhNMnh5WldFeVkzQlllakprTTJ3NGQyZFJZVWRyWWpsdGFHSmhSRklLU1RWTFNXSk5LMGxVY1RJMGNGWkRkbXg1VkdwUU4xRTNZMWd6VTJoSFVFeFZiR0pzTWxGQmNFTjZTbnBNVUROWFpVdFBRMEpvYTNkbloxbFdUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlZOTUVoWENuQklUMHAzVm1oT01sWjJjaTgzWkcweVNsUlZlVGM0ZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDJSQldVUldVakJTUVZGSUwwSkhiM2RoU1ZwdFlVaFNNR05JVFRaTWVUbHVZVmhTYjJSWFNYVlpNamwwVERKV2JGcFhNVFpqZVRsb1lqTkNlZ3BNV0VKeldWaFNiV0l6U25STWVUVnVZVmhTYjJSWFNYWmtNamw1WVRKYWMySXpaSHBNTTA1d1dqSTBkR05JVm1saVIyeHFURmRHYmxwWE5UQk1WMFo2Q21NeVZqQmplVFUxWWxkNFFXTnRWbTFqZVRsdldsZEdhMk41T1hSWlYyeDFUVVJyUjBOcGMwZEJVVkZDWnpjNGQwRlJSVVZMTW1nd1pFaENlazlwT0hZS1pFYzVjbHBYTkhWWlYwNHdZVmM1ZFdONU5XNWhXRkp2WkZkS01XTXlWbmxaTWpsMVpFZFdkV1JETldwaU1qQjNTSGRaUzB0M1dVSkNRVWRFZG5wQlFncEJaMUZTWkRJNWVXRXlXbk5pTTJSbVdrZHNlbU5IUmpCWk1tZDNUbWRaUzB0M1dVSkNRVWRFZG5wQlFrRjNVVzlPYWtFMFQwUlJlVnBxUVRWYWFtc3dDazU2Ykd0T1ZGRjVXVzFHYWsxWFNtbE9iVVpvVGxSQmVrNUhVbXROUkVGM1RXMWFhMDVFUVhoQ1oyOXlRbWRGUlVGWlR5OU5RVVZGUWtOT1ZHRlhaSFVLU1VkV05GbFhUakJKU0VJeFdXMTRjRmw1UWtKVU1VSlVTVWRHYmxwWE5UQkpSMFo2WXpKV01HTjZRV2xDWjI5eVFtZEZSVUZaVHk5TlFVVkdRa0pTYkFwYVYxWjBaVzVOZGxsWE9YZGplVEYzWWtkR01GcHRPWGxpVkVGa1FtZHZja0puUlVWQldVOHZUVUZGUjBKQk9YbGFWMXA2VERKb2JGbFhVbnBNTWpGb0NtRlhOSGRQZDFsTFMzZFpRa0pCUjBSMmVrRkNRMEZSZEVSRGRHOWtTRkozWTNwdmRrd3pVblpoTWxaMVRHMUdhbVJIYkhaaWJrMTFXakpzTUdGSVZta0taRmhPYkdOdFRuWmlibEpzWW01UmRWa3lPWFJOU0ZsSFEybHpSMEZSVVVKbk56aDNRVkZyUldGQmVHMWhTRkl3WTBoTk5reDVPVzVoV0ZKdlpGZEpkUXBaTWpsMFRESldiRnBYTVRaamVUbG9Zak5DZWt4WVFuTlpXRkp0WWpOS2RFeDVOVzVoV0ZKdlpGZEpkbVF5T1hsaE1scHpZak5rZWt3elRuQmFNalIwQ21OSVZtbGlSMnhxVEZkR2JscFhOVEJNVjBaNll6SldNR041TlRWaVYzaEJZMjFXYldONU9XOWFWMFpyWTNrNWRGbFhiSFZOUkdkSFEybHpSMEZSVVVJS1p6YzRkMEZSYjBWTFozZHZUbXBCTkU5RVVYbGFha0UxV21wck1FNTZiR3RPVkZGNVdXMUdhazFYU21sT2JVWm9UbFJCZWs1SFVtdE5SRUYzVFcxYWF3cE9SRUZrUW1kdmNrSm5SVVZCV1U4dlRVRkZURUpCT0UxRVYyUndaRWRvTVZscE1XOWlNMDR3V2xkUmQwNTNXVXRMZDFsQ1FrRkhSSFo2UVVKRVFWRndDa1JEWkc5a1NGSjNZM3B2ZGt3eVpIQmtSMmd4V1drMWFtSXlNSFphVjFac1lsaHdla3d5Um5aalNFMTBZMGQ0YUdSSFduWmpiVEIzVDBGWlMwdDNXVUlLUWtGSFJIWjZRVUpFVVZGeFJFTm5NazFFWnpST1JFcHRUVVJzYlU5VVVUTlBWMUV4VGtSS2FWbFhUWGhaYlVreVdWZEZNVTFFVFRCYVIxRjNUVVJCZVFwYWJWRXdUVUk0UjBOcGMwZEJVVkZDWnpjNGQwRlJORVZGVVhkUVkyMVdiV041T1c5YVYwWnJZM2s1ZEZsWGJIVk5RbTlIUTJselIwRlJVVUpuTnpoM0NrRlJPRVZFUVhkTFRWUk5lRTVxU1RGT1ZFRjVUbXBCY0VKbmIzSkNaMFZGUVZsUEwwMUJSVkZDUW5OTlIxZG9NR1JJUW5wUGFUaDJXakpzTUdGSVZta0tURzFPZG1KVE9XeGFWMVowWlc1TmQwZFJXVXRMZDFsQ1FrRkhSSFo2UVVKRlVWRk1SRUZyZUUxRWEzcE5WRUV6VG5wUmQyUm5XVXRMZDFsQ1FrRkhSQXAyZWtGQ1JXZFNiMFJIV205a1NGSjNZM3B2ZGt3eVpIQmtSMmd4V1drMWFtSXlNSFphVjFac1lsaHdla3d5Um5aalNFMTBZMGQ0YUdSSFduWmpiVEIyQ2t4dFpIQmtSMmd4V1drNU0ySXpTbkphYlhoMlpETk5kbU15Ykc1aWFURjNaRmRLYzJGWFRYUlpWMlJzWW01UmRGbFlUbnBhV0ZKNlRHNXNkR0pGUW5rS1dsZGFla3d5YUd4WlYxSjZUREl4YUdGWE5IZFBRVmxMUzNkWlFrSkJSMFIyZWtGQ1JYZFJjVVJEWnpKTlJHYzBUa1JLYlUxRWJHMVBWRkV6VDFkUk1RcE9SRXBwV1ZkTmVGbHRTVEpaVjBVeFRVUk5NRnBIVVhkTlJFRjVXbTFSTUUxRFJVZERhWE5IUVZGUlFtYzNPSGRCVWxGRlJYZDNVbVF5T1hsaE1scHpDbUl6WkdaYVIyeDZZMGRHTUZreVozZFhkMWxMUzNkWlFrSkJSMFIyZWtGQ1JsRlNUa1JGZEc5a1NGSjNZM3B2ZGt3eVpIQmtSMmd4V1drMWFtSXlNSFlLV2xkV2JHSlljSHBNTWtaMlkwaE5kR05IZUdoa1IxcDJZMjB3ZGxsWFRqQmhWemwxWTNrNWVXUlhOWHBNZWsxNVRWUkpOVTFFVVRGT1JFRXlUREpHTUFwa1IxWjBZMGhTZWt4NlJYZEdkMWxMUzNkWlFrSkJSMFIyZWtGQ1JtZFJTa1JCWkhkamJXd3lXVmhTYkUxR1NVZERhWE5IUVZGUlFtYzNPSGRCVW1kRkNsSkJlRU5qYlZaM1lucHdiRnBYVm5SbGJrNUJUVlJCTlUxNlJYZE9lbU13VERKR2RtTklUWFJqUjNob1pFZGFkbU50TVVGTlZFMTRUbXBKTVU1VVFYa0tUbXB3ZVZwWFdUWmpiVlp0WTNrNWIxcFhSbXRqZVRsMFdWZHNkVTFKUjB0Q1oyOXlRbWRGUlVGa1dqVkJaMUZEUWtoM1JXVm5RalJCU0ZsQk0xUXdkd3BoYzJKSVJWUktha2RTTkdOdFYyTXpRWEZLUzFoeWFtVlFTek12YURSd2VXZERPSEEzYnpSQlFVRkhaMFpKU3l0RFFVRkJRa0ZOUVZKNlFrWkJhVVZCQ25rNVMyTlpOMjlNVTBKR1pUaG5WMjFEV0VoaGFuZHJNelJzY2tkTGJWTlphVGNyV2pCM1dWcDNVRFJEU1VWTVlXNXJWV1pYWW5Sdk1HZGhLelIzVFRnS1lYQkpielpITjJvcmNVNVpaV3d2VEdnMFVISm1VR3h0VFVGdlIwTkRjVWRUVFRRNVFrRk5SRUV5WjBGTlIxVkRUVkZEWWtsRmRTOU1jMFI2VldOMlVRcEtiMm9yZGtjeWQzUXdOR2h1TlUxR1RteFZhbWxETTFKVFlucENVbFZXVjBJMk9VSnZjSG8yWkU5d1ptTkhlVk5DY0RoRFRVZ3ZjMDVyYWxCV1QwNXJDbkZsYjBaUmVVMU9LM294WkhJMlNuUkxkSFZvUW1Ga01FVlpRakJuV0dWaWJFVldTM2hTZDFweFJIRktSM1ZuV1hSMk4zSndaejA5Q2kwdExTMHRSVTVFSUVORlVsUkpSa2xEUVZSRkxTMHRMUzBLIn19fX0="
57
54
  }
58
55
  ],
59
56
  "timestampVerificationData": {}
@@ -61,8 +58,8 @@
61
58
  "messageSignature": {
62
59
  "messageDigest": {
63
60
  "algorithm": "SHA2_256",
64
- "digest": "OT37QOSCiNxEZQoUxJDUlzBDLdvMLYM+lLJ54yq4JRM="
61
+ "digest": "PNTGOyfHqrVO8Kzp681UnIJmLnXVNuxkwnJz/UoFDjU="
65
62
  },
66
- "signature": "MEUCIHs2kxfCtoCR8r85zkmGM0/9o4gFC6aaCE8kCn8oYIvWAiEAhmJbS9NHoHxzWbbs54Qy9trAUL3ARbt6fgWisvRMsWk="
63
+ "signature": "MEUCIQCiogxGMG2I+gVnvpMjxTkB8q4vpG7LQOB2DuRUov3BewIgPdmyZJPjwJfrlp7Qk34i9m7xXYEyINOK6FnjtW1th48="
67
64
  }
68
65
  }
@@ -29,11 +29,13 @@ const GLOBAL_UPDATE_EXECUTOR_MAX_BYTES = 512 * 1024 * 1024;
29
29
  class ChildCommandError extends Error {
30
30
  status;
31
31
  diagnostic;
32
- constructor(command, status, diagnostic) {
32
+ stdout;
33
+ constructor(command, status, diagnostic, stdout = '') {
33
34
  super(`${path.basename(command)} failed with status ${status ?? 'unknown'}`);
34
35
  this.name = 'ChildCommandError';
35
36
  this.status = status;
36
37
  this.diagnostic = diagnostic;
38
+ this.stdout = stdout;
37
39
  }
38
40
  }
39
41
  function cleanEnvironment() {
@@ -69,7 +71,7 @@ function defaultRunner(command, args, cwd = process.cwd()) {
69
71
  }
70
72
  catch {
71
73
  }
72
- throw new ChildCommandError(command, result.status, diagnostic);
74
+ throw new ChildCommandError(command, result.status, diagnostic, result.stdout ?? '');
73
75
  }
74
76
  return result.stdout;
75
77
  }
@@ -280,10 +282,33 @@ export function inspectGlobalUpdate(run = defaultRunner) {
280
282
  function runInstalledJson(run, cliPath, args) {
281
283
  return parseJson(run(process.execPath, [cliPath, ...args]).trim(), args.join('_'));
282
284
  }
285
+ function childCommandStdout(error) {
286
+ const stdout = error?.stdout;
287
+ return typeof stdout === 'string' && stdout.trim() !== '' ? stdout : undefined;
288
+ }
289
+ function readServiceStatus(run, cliPath, service) {
290
+ try {
291
+ return runInstalledJson(run, cliPath, [service, 'status', '--json']);
292
+ }
293
+ catch (error) {
294
+ const stdout = childCommandStdout(error);
295
+ if (stdout === undefined)
296
+ throw error;
297
+ try {
298
+ const parsed = JSON.parse(stdout.trim());
299
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
300
+ throw error;
301
+ return parsed;
302
+ }
303
+ catch {
304
+ throw error;
305
+ }
306
+ }
307
+ }
283
308
  function serviceSnapshot(run, cliPath) {
284
309
  return {
285
- server: runInstalledJson(run, cliPath, ['server', 'status', '--json']),
286
- cockpit: runInstalledJson(run, cliPath, ['cockpit', 'status', '--json']),
310
+ server: readServiceStatus(run, cliPath, 'server'),
311
+ cockpit: readServiceStatus(run, cliPath, 'cockpit'),
287
312
  };
288
313
  }
289
314
  function assertHealthyServices(services) {
@@ -565,6 +590,7 @@ export function dispatchGlobalUpdate(options, run = defaultRunner) {
565
590
  rollbackPerformed: false,
566
591
  error: safeUpdateError(error),
567
592
  events: [],
593
+ eventTimes: [],
568
594
  });
569
595
  }
570
596
  throw new Error(`global update candidate executor failed; diagnostic record: ${resultPath}`, { cause: error });
@@ -652,24 +678,161 @@ function rollbackServerApplication(run, cliPath, updateId, serverPath, expectedR
652
678
  const defaultExecutionDependencies = {
653
679
  fetch: globalThis.fetch.bind(globalThis),
654
680
  sleep: (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)),
681
+ now: () => new Date(),
655
682
  };
683
+ class GlobalUpdateEventLog {
684
+ now;
685
+ names = [];
686
+ times = [];
687
+ constructor(now) {
688
+ this.now = now;
689
+ }
690
+ push(event) {
691
+ this.names.push(event);
692
+ this.times.push(this.now().toISOString());
693
+ }
694
+ includes(event) {
695
+ return this.names.includes(event);
696
+ }
697
+ get events() {
698
+ return [...this.names];
699
+ }
700
+ get eventTimes() {
701
+ return [...this.times];
702
+ }
703
+ }
656
704
  class StartupHealthPending extends Error {
657
- constructor(cause) {
705
+ detail;
706
+ constructor(cause, detail) {
658
707
  super('global_update_health_startup_pending', { cause });
708
+ this.detail = detail;
709
+ }
710
+ }
711
+ function serviceHealthFacts(services) {
712
+ const liveness = services.server.liveness;
713
+ return {
714
+ serverState: services.server.runtimeState,
715
+ serverHealth: liveness?.health,
716
+ serverReason: services.server.error,
717
+ cockpitState: services.cockpit.runtimeState,
718
+ cockpitHealth: services.cockpit.health,
719
+ cockpitReason: services.cockpit.reason,
720
+ };
721
+ }
722
+ const HEALTH_SUBJECTS = new Set([
723
+ 'install-readback', 'server-status', 'cockpit-status', 'service-health',
724
+ 'server', 'cockpit', 'cockpit-proxy',
725
+ ]);
726
+ const HEALTH_PHASES = new Set(['startup', 'steady']);
727
+ const SAFE_STATE = /^[a-z][a-z0-9-]{0,40}$/u;
728
+ const SAFE_REASON_CODE = /\b(?:aops|community|global_update)_[a-z0-9_]+\b/iu;
729
+ function sanitizeHealthDetail(detail) {
730
+ const safe = {
731
+ subject: typeof detail.subject === 'string' && HEALTH_SUBJECTS.has(detail.subject)
732
+ ? detail.subject
733
+ : 'unrecognized',
734
+ };
735
+ if (typeof detail.phase === 'string' && HEALTH_PHASES.has(detail.phase))
736
+ safe.phase = detail.phase;
737
+ if (typeof detail.reachable === 'boolean')
738
+ safe.reachable = detail.reachable;
739
+ for (const key of ['sample', 'attempts', 'childStatus', 'status']) {
740
+ const value = detail[key];
741
+ if (typeof value === 'number' && Number.isSafeInteger(value))
742
+ safe[key] = value;
743
+ }
744
+ for (const key of ['serverState', 'serverHealth', 'cockpitState', 'cockpitHealth']) {
745
+ const value = detail[key];
746
+ if (value === undefined || value === null)
747
+ continue;
748
+ safe[key] = typeof value === 'string' && SAFE_STATE.test(value) ? value : 'unrecognized';
659
749
  }
750
+ for (const key of ['serverReason', 'cockpitReason']) {
751
+ const value = detail[key];
752
+ if (value === undefined || value === null)
753
+ continue;
754
+ safe[key] = typeof value === 'string'
755
+ ? SAFE_REASON_CODE.exec(value)?.[0]?.toLowerCase() ?? 'unrecognized'
756
+ : 'unrecognized';
757
+ }
758
+ return safe;
759
+ }
760
+ function childCommandStatus(error) {
761
+ const status = error?.status;
762
+ return typeof status === 'number' ? status : undefined;
763
+ }
764
+ class ForwardSchemaRetainsTarget extends Error {
765
+ applicationRollbackError;
766
+ constructor(applicationRollbackError) {
767
+ super('global_update_forward_schema_retains_target');
768
+ this.applicationRollbackError = applicationRollbackError;
769
+ }
770
+ }
771
+ export class StableHealthFailure extends Error {
772
+ healthFailure;
773
+ constructor(detail) {
774
+ const safe = sanitizeHealthDetail(detail);
775
+ super(`global_update_stable_health_failed:${String(safe.subject)}`);
776
+ this.healthFailure = Object.freeze(safe);
777
+ }
778
+ }
779
+ export function resolveRecoveryClosure(state) {
780
+ if (!state.applicationSideEffectPossible)
781
+ return 'prior';
782
+ return state.applicationRolledBack ? 'prior' : 'target';
783
+ }
784
+ const APPLICATION_PRE_EFFECT_CODES = new Set([
785
+ 'community_native_application_update_content_unchanged',
786
+ ]);
787
+ export function applicationSideEffectPossible(attempted, updateError) {
788
+ if (!attempted)
789
+ return false;
790
+ const diagnostic = nestedCommunityDiagnostic(updateError);
791
+ if (!diagnostic)
792
+ return true;
793
+ const cause = diagnostic.cause;
794
+ const codes = [diagnostic.code, cause?.code];
795
+ return !codes.some((code) => typeof code === 'string' && APPLICATION_PRE_EFFECT_CODES.has(code));
660
796
  }
661
797
  async function assertStableHealth(run, plan, execution) {
662
798
  const evidence = [];
799
+ const fail = (error, startup, detail) => {
800
+ if (startup)
801
+ throw new StartupHealthPending(error, detail);
802
+ throw new StableHealthFailure({ ...detail, phase: 'steady' });
803
+ };
663
804
  const collectSample = async (sample, startup) => {
664
- const installed = inspectGlobalInstall(run);
805
+ let installed;
806
+ try {
807
+ installed = inspectGlobalInstall(run);
808
+ }
809
+ catch (error) {
810
+ fail(error, startup, { subject: 'install-readback', sample, childStatus: childCommandStatus(error) });
811
+ throw error;
812
+ }
665
813
  let services;
666
814
  try {
667
- services = serviceSnapshot(run, installed.cliPath);
815
+ services = {
816
+ server: readServiceStatus(run, installed.cliPath, 'server'),
817
+ cockpit: { runtimeState: 'not-read' },
818
+ };
819
+ }
820
+ catch (error) {
821
+ fail(error, startup, { subject: 'server-status', sample, childStatus: childCommandStatus(error) });
822
+ throw error;
823
+ }
824
+ try {
825
+ services = { ...services, cockpit: readServiceStatus(run, installed.cliPath, 'cockpit') };
826
+ }
827
+ catch (error) {
828
+ fail(error, startup, { subject: 'cockpit-status', sample, childStatus: childCommandStatus(error) });
829
+ throw error;
830
+ }
831
+ try {
668
832
  assertHealthyServices(services);
669
833
  }
670
834
  catch (error) {
671
- if (startup)
672
- throw new StartupHealthPending(error);
835
+ fail(error, startup, { subject: 'service-health', sample, ...serviceHealthFacts(services) });
673
836
  throw error;
674
837
  }
675
838
  const serverOrigin = validateLoopbackOrigin(services.server.origin, 'AOPS Server origin');
@@ -685,14 +848,12 @@ async function assertStableHealth(run, plan, execution) {
685
848
  response = await execution.fetch(url, { cache: 'no-store', signal: AbortSignal.timeout(15_000) });
686
849
  }
687
850
  catch (error) {
688
- if (startup)
689
- throw new StartupHealthPending(error);
851
+ fail(error, startup, { subject: name, sample, reachable: false });
690
852
  throw error;
691
853
  }
692
- if (response.status !== 200 && startup) {
693
- throw new StartupHealthPending(new Error(`${name} health returned ${response.status}`));
854
+ if (response.status !== 200) {
855
+ fail(new Error(`${name} health returned ${response.status}`), startup, { subject: name, sample, reachable: true, status: response.status });
694
856
  }
695
- assert.equal(response.status, 200, `${name} health returned ${response.status}`);
696
857
  const body = await response.arrayBuffer();
697
858
  endpoints.push({ name, url, status: response.status, bytes: body.byteLength });
698
859
  }
@@ -708,7 +869,11 @@ async function assertStableHealth(run, plan, execution) {
708
869
  catch (error) {
709
870
  if (!(error instanceof StartupHealthPending))
710
871
  throw error;
711
- startupError = error.cause ?? error;
872
+ startupError = new StableHealthFailure({
873
+ ...error.detail,
874
+ phase: 'startup',
875
+ attempts: attempt,
876
+ });
712
877
  if (attempt < plan.safety.healthStartupAttempts) {
713
878
  await execution.sleep(plan.safety.healthStartupIntervalMs);
714
879
  }
@@ -755,6 +920,9 @@ function nestedCommunityDiagnostic(error) {
755
920
  return undefined;
756
921
  }
757
922
  function safeUpdateError(error) {
923
+ if (error instanceof StableHealthFailure) {
924
+ return { code: 'global_update_stable_health_failed', healthFailure: error.healthFailure };
925
+ }
758
926
  const diagnostic = nestedCommunityDiagnostic(error);
759
927
  if (diagnostic) {
760
928
  const details = diagnostic.details;
@@ -785,6 +953,160 @@ function writeTerminalUpdateResult(plan, result) {
785
953
  const resultPath = path.join(plan.operationRoot, RESULT_FILE);
786
954
  writeFileSync(resultPath, `${JSON.stringify(result, null, 2)}\n`, { flag: 'wx', mode: 0o600 });
787
955
  }
956
+ const RECOVERY_CONTRACT = 'aops.global-update-recovery/v1';
957
+ const RECOVERY_RESULT_PATTERN = /^update-recovery-(0|[1-9]\d*)\.json$/u;
958
+ const RECOVERABLE_TERMINAL_STATUSES = new Set(['recovery-required']);
959
+ function recoveryAttemptFiles(plan) {
960
+ const operationRoot = plainDirectory(plan.operationRoot, 'global update operation root');
961
+ const attempts = [];
962
+ for (const entry of readdirSync(operationRoot)) {
963
+ const match = RECOVERY_RESULT_PATTERN.exec(entry);
964
+ if (match)
965
+ attempts.push({ attempt: Number(match[1]), path: path.join(operationRoot, entry) });
966
+ }
967
+ return attempts.sort((left, right) => left.attempt - right.attempt);
968
+ }
969
+ function nextRecoveryResultPath(plan) {
970
+ const attempts = recoveryAttemptFiles(plan);
971
+ const next = attempts.length === 0 ? 1 : attempts[attempts.length - 1].attempt + 1;
972
+ return path.join(plan.operationRoot, `update-recovery-${next}.json`);
973
+ }
974
+ function sameClosure(installed, expected) {
975
+ try {
976
+ assert.deepEqual(installed, expected);
977
+ return true;
978
+ }
979
+ catch {
980
+ return false;
981
+ }
982
+ }
983
+ function writeRecoveryResult(resultPath, result) {
984
+ writeFileSync(resultPath, `${JSON.stringify(result, null, 2)}\n`, { flag: 'wx', mode: 0o600 });
985
+ }
986
+ function preflightGlobalUpdateRecovery(options, run) {
987
+ const plan = readGlobalUpdatePlan(options.plan);
988
+ assert.equal(options.confirm, plan.planId, 'global update recovery confirmation does not match the exact plan');
989
+ const terminalPath = path.join(plan.operationRoot, RESULT_FILE);
990
+ assert.equal(existsSync(terminalPath), true, 'global_update_recovery_requires_terminal_result');
991
+ const terminal = parseJson(readFileSync(terminalPath, 'utf8'), 'global_update_result');
992
+ assert.equal(terminal.planId, plan.planId, 'terminal result belongs to a different plan');
993
+ const terminalStatus = String(terminal.status);
994
+ assert.equal(RECOVERABLE_TERMINAL_STATUSES.has(terminalStatus), true, `global_update_recovery_status_not_recoverable:${terminalStatus}`);
995
+ const installed = inspectGlobalInstall(run);
996
+ assert.equal(installed.globalPrefix, plan.globalPrefix, 'global npm prefix changed after update planning');
997
+ const installedClosure = [installed.packages, installed.topology];
998
+ const atTarget = sameClosure(installedClosure, [plan.target.packages, plan.target.topology]);
999
+ const atPrior = sameClosure(installedClosure, [plan.prior.packages, plan.prior.topology]);
1000
+ assert.equal(atTarget || atPrior, true, 'global_update_recovery_closure_drifted');
1001
+ const succeeded = recoveryAttemptFiles(plan)
1002
+ .map((file) => ({
1003
+ ...file,
1004
+ record: parseJson(readFileSync(file.path, 'utf8'), 'global_update_recovery_result'),
1005
+ }))
1006
+ .find((entry) => entry.record.status === 'recovered');
1007
+ if (succeeded) {
1008
+ assert.equal(atTarget, true, 'global_update_recovery_recorded_but_closure_moved');
1009
+ }
1010
+ return { plan, terminalStatus, installed, atTarget, atPrior, succeeded };
1011
+ }
1012
+ export function dispatchGlobalUpdateRecovery(options, run = defaultRunner) {
1013
+ const preflight = preflightGlobalUpdateRecovery(options, run);
1014
+ const { plan } = preflight;
1015
+ if (preflight.succeeded)
1016
+ return preflight.succeeded.record;
1017
+ const resultPath = nextRecoveryResultPath(plan);
1018
+ let executorStarted = false;
1019
+ try {
1020
+ const { receipt, receiptPath } = stageCandidateGlobalUpdateExecutor(plan, run);
1021
+ executorStarted = true;
1022
+ return parseJson(run(process.execPath, [
1023
+ receipt.entrypoint,
1024
+ 'update',
1025
+ 'recover',
1026
+ '--plan',
1027
+ options.plan,
1028
+ '--confirm',
1029
+ options.confirm,
1030
+ '--executor-receipt',
1031
+ receiptPath,
1032
+ '--json',
1033
+ ]).trim(), 'global_update_recovery_result');
1034
+ }
1035
+ catch (error) {
1036
+ if (!existsSync(resultPath)) {
1037
+ writeRecoveryResult(resultPath, {
1038
+ schemaVersion: 1,
1039
+ contract: RECOVERY_CONTRACT,
1040
+ status: executorStarted ? 'executor-result-missing' : 'executor-staging-failed',
1041
+ planId: plan.planId,
1042
+ failedStage: executorStarted ? 'candidate-executor' : 'stage-candidate-executor',
1043
+ globalState: executorStarted ? 'unknown-requires-inspection' : 'unchanged',
1044
+ error: safeUpdateError(error),
1045
+ events: [],
1046
+ eventTimes: [],
1047
+ });
1048
+ }
1049
+ throw new Error(`global update recovery executor failed; diagnostic record: ${resultPath}`, { cause: error });
1050
+ }
1051
+ }
1052
+ export async function recoverGlobalUpdate(options, run = defaultRunner, execution = defaultExecutionDependencies) {
1053
+ const preflight = preflightGlobalUpdateRecovery(options, run);
1054
+ const { plan, installed: current, atTarget, terminalStatus } = preflight;
1055
+ const installedCliRoot = plainDirectory(path.dirname(path.dirname(current.cliPath)), 'installed global CLI root');
1056
+ const invokedEntrypoint = plainFile(process.argv[1], 'invoked AOPS CLI entrypoint');
1057
+ const relativeToInstalled = path.relative(installedCliRoot, invokedEntrypoint);
1058
+ assert.equal(relativeToInstalled.startsWith('..') || path.isAbsolute(relativeToInstalled), true, 'global_update_recovery_must_not_run_from_the_replaced_cli');
1059
+ if (preflight.succeeded)
1060
+ return preflight.succeeded.record;
1061
+ const resultPath = nextRecoveryResultPath(plan);
1062
+ const events = new GlobalUpdateEventLog(execution.now ?? (() => new Date()));
1063
+ try {
1064
+ stopService(run, current.cliPath, 'cockpit', true);
1065
+ stopService(run, current.cliPath, 'server', true);
1066
+ if (atTarget) {
1067
+ events.push('retained:target-closure');
1068
+ }
1069
+ else {
1070
+ installGlobalUpdateClosure(run, plan, plan.candidates, plan.target.topology);
1071
+ events.push('installed:target-closure');
1072
+ }
1073
+ const restored = inspectGlobalInstall(run);
1074
+ assert.deepEqual(restored.packages, plan.target.packages, 'recovered closure is not the exact target');
1075
+ assert.deepEqual(restored.topology, plan.target.topology, 'recovered topology is not canonical');
1076
+ startService(run, restored.cliPath, 'server');
1077
+ events.push('started:server');
1078
+ startService(run, restored.cliPath, 'cockpit');
1079
+ events.push('started:cockpit');
1080
+ const healthEvidence = await assertStableHealth(run, plan, execution);
1081
+ const result = {
1082
+ schemaVersion: 1,
1083
+ contract: RECOVERY_CONTRACT,
1084
+ status: 'recovered',
1085
+ planId: plan.planId,
1086
+ installed: plan.target.packages,
1087
+ recoveredFrom: terminalStatus,
1088
+ events: events.events,
1089
+ eventTimes: events.eventTimes,
1090
+ healthEvidence,
1091
+ };
1092
+ writeRecoveryResult(resultPath, result);
1093
+ return result;
1094
+ }
1095
+ catch (recoveryError) {
1096
+ writeRecoveryResult(resultPath, {
1097
+ schemaVersion: 1,
1098
+ contract: RECOVERY_CONTRACT,
1099
+ status: 'recovery-failed',
1100
+ planId: plan.planId,
1101
+ recoveredFrom: terminalStatus,
1102
+ globalState: 'unknown-requires-inspection',
1103
+ error: safeUpdateError(recoveryError),
1104
+ events: events.events,
1105
+ eventTimes: events.eventTimes,
1106
+ });
1107
+ throw recoveryError;
1108
+ }
1109
+ }
788
1110
  export async function applyGlobalUpdate(options, run = defaultRunner, execution = defaultExecutionDependencies) {
789
1111
  const plan = readGlobalUpdatePlan(options.plan);
790
1112
  assert.equal(options.confirm, plan.planId, 'global update confirmation does not match the exact plan');
@@ -794,8 +1116,9 @@ export async function applyGlobalUpdate(options, run = defaultRunner, execution
794
1116
  assert.deepEqual(current.topology, plan.prior.topology, 'global package topology changed after update planning');
795
1117
  assert.equal(current.globalPrefix, plan.globalPrefix, 'global npm prefix changed after update planning');
796
1118
  assertHealthyServices(serviceSnapshot(run, current.cliPath));
797
- const events = [];
1119
+ const events = new GlobalUpdateEventLog(execution.now ?? (() => new Date()));
798
1120
  let applicationUpdateId = null;
1121
+ let applicationUpdateAttempted = false;
799
1122
  let failedStage = 'stop-cockpit';
800
1123
  try {
801
1124
  stopService(run, current.cliPath, 'cockpit');
@@ -817,6 +1140,7 @@ export async function applyGlobalUpdate(options, run = defaultRunner, execution
817
1140
  }
818
1141
  else {
819
1142
  failedStage = 'update-server-application';
1143
+ applicationUpdateAttempted = true;
820
1144
  applicationUpdateId = updateServerApplication(run, updated.cliPath, {
821
1145
  prior: plan.prior.packages.server,
822
1146
  target: plan.target.packages.server,
@@ -835,7 +1159,8 @@ export async function applyGlobalUpdate(options, run = defaultRunner, execution
835
1159
  planId: plan.planId,
836
1160
  installed: updated.packages,
837
1161
  rollbackPerformed: false,
838
- events,
1162
+ events: events.events,
1163
+ eventTimes: events.eventTimes,
839
1164
  healthEvidence,
840
1165
  next: 'Exit and reopen the AOPS TUI to run the newly installed binary.',
841
1166
  };
@@ -844,6 +1169,7 @@ export async function applyGlobalUpdate(options, run = defaultRunner, execution
844
1169
  }
845
1170
  catch (updateError) {
846
1171
  const updateStage = failedStage;
1172
+ const sideEffectPossible = applicationSideEffectPossible(applicationUpdateAttempted, updateError);
847
1173
  events.push('rollback:started');
848
1174
  let rollbackError;
849
1175
  try {
@@ -854,19 +1180,89 @@ export async function applyGlobalUpdate(options, run = defaultRunner, execution
854
1180
  const restored = inspectGlobalInstall(run);
855
1181
  assert.deepEqual(restored.packages, plan.prior.packages, 'rollback package closure is not exact');
856
1182
  assert.deepEqual(restored.topology, plan.prior.topology, 'rollback package topology is not exact');
1183
+ let applicationRollbackError;
857
1184
  if (applicationUpdateId) {
858
- rollbackServerApplication(run, restored.cliPath, applicationUpdateId, restored.serverPath, plan.prior.packages.server);
859
- events.push('rolled-back:server-application');
1185
+ try {
1186
+ rollbackServerApplication(run, restored.cliPath, applicationUpdateId, restored.serverPath, plan.prior.packages.server);
1187
+ events.push('rolled-back:server-application');
1188
+ }
1189
+ catch (error) {
1190
+ applicationRollbackError = error;
1191
+ events.push('failed:server-application-rollback');
1192
+ }
1193
+ }
1194
+ else if (sideEffectPossible) {
1195
+ applicationRollbackError = new Error('global_update_application_outcome_unrecorded');
1196
+ events.push('failed:server-application-outcome-unrecorded');
860
1197
  }
861
1198
  else {
862
1199
  startService(run, restored.cliPath, 'server');
863
1200
  events.push('started:server-restore');
864
1201
  }
1202
+ if (resolveRecoveryClosure({
1203
+ applicationSideEffectPossible: sideEffectPossible,
1204
+ applicationRolledBack: applicationUpdateId !== null && applicationRollbackError === undefined,
1205
+ }) === 'target') {
1206
+ throw new ForwardSchemaRetainsTarget(applicationRollbackError);
1207
+ }
865
1208
  startService(run, restored.cliPath, 'cockpit');
866
1209
  await assertStableHealth(run, plan, execution);
867
1210
  events.push('rollback:healthy');
868
1211
  }
869
1212
  catch (rollbackError) {
1213
+ if (rollbackError instanceof ForwardSchemaRetainsTarget) {
1214
+ try {
1215
+ installGlobalUpdateClosure(run, plan, plan.candidates, plan.target.topology);
1216
+ events.push('reinstalled:target-closure');
1217
+ const retained = inspectGlobalInstall(run);
1218
+ assert.deepEqual(retained.packages, plan.target.packages, 'retained target closure is not exact');
1219
+ assert.deepEqual(retained.topology, plan.target.topology, 'retained target topology is not canonical');
1220
+ startService(run, retained.cliPath, 'server');
1221
+ events.push('started:server-target-retained');
1222
+ startService(run, retained.cliPath, 'cockpit');
1223
+ events.push('started:cockpit-target-retained');
1224
+ await assertStableHealth(run, plan, execution);
1225
+ events.push('target-retained:healthy');
1226
+ }
1227
+ catch (retainError) {
1228
+ writeTerminalUpdateResult(plan, {
1229
+ schemaVersion: 1,
1230
+ contract: RESULT_CONTRACT,
1231
+ status: 'recovery-required',
1232
+ planId: plan.planId,
1233
+ failedStage: updateStage,
1234
+ rollbackPerformed: false,
1235
+ rollback: {
1236
+ status: 'failed',
1237
+ reason: 'application_rollback_failed_forward_schema',
1238
+ error: safeUpdateError(rollbackError.applicationRollbackError),
1239
+ },
1240
+ targetRetention: { status: 'failed', error: safeUpdateError(retainError) },
1241
+ error: safeUpdateError(updateError),
1242
+ events: events.events,
1243
+ eventTimes: events.eventTimes,
1244
+ });
1245
+ throw new AggregateError([updateError, rollbackError.applicationRollbackError, retainError], `global update failed, application rollback failed, and the schema-compatible target closure could not be restarted; services may remain stopped; recovery artifacts remain at ${plan.operationRoot}`);
1246
+ }
1247
+ writeTerminalUpdateResult(plan, {
1248
+ schemaVersion: 1,
1249
+ contract: RESULT_CONTRACT,
1250
+ status: 'failed-target-retained',
1251
+ planId: plan.planId,
1252
+ failedStage: updateStage,
1253
+ installed: plan.target.packages,
1254
+ rollbackPerformed: false,
1255
+ rollback: {
1256
+ status: 'not-applicable',
1257
+ reason: 'application_rollback_failed_forward_schema',
1258
+ applicationRollbackError: safeUpdateError(rollbackError.applicationRollbackError),
1259
+ },
1260
+ error: safeUpdateError(updateError),
1261
+ events: events.events,
1262
+ eventTimes: events.eventTimes,
1263
+ });
1264
+ throw new AggregateError([updateError, rollbackError.applicationRollbackError], 'global update failed and the application could not be rolled back; the schema-compatible target closure was reinstated and healthy services were restarted');
1265
+ }
870
1266
  try {
871
1267
  const restoredForRecovery = inspectGlobalInstall(run);
872
1268
  assert.deepEqual(restoredForRecovery.packages, plan.prior.packages, 'service recovery package closure is not exact');
@@ -891,7 +1287,8 @@ export async function applyGlobalUpdate(options, run = defaultRunner, execution
891
1287
  rollback: { status: 'failed', error: safeUpdateError(rollbackError) },
892
1288
  serviceRecovery: { status: 'failed', error: safeUpdateError(serviceRecoveryError) },
893
1289
  error: safeUpdateError(updateError),
894
- events,
1290
+ events: events.events,
1291
+ eventTimes: events.eventTimes,
895
1292
  });
896
1293
  throw new AggregateError([updateError, rollbackError, serviceRecoveryError], `global update, automatic rollback, and service recovery failed; services may remain stopped; recovery artifacts remain at ${plan.operationRoot}`);
897
1294
  }
@@ -909,7 +1306,8 @@ export async function applyGlobalUpdate(options, run = defaultRunner, execution
909
1306
  applicationRollbackError: safeUpdateError(rollbackError),
910
1307
  },
911
1308
  error: safeUpdateError(updateError),
912
- events,
1309
+ events: events.events,
1310
+ eventTimes: events.eventTimes,
913
1311
  });
914
1312
  throw new AggregateError([updateError, rollbackError], `global update and application rollback failed; prior exact packages and healthy services were restored; recovery artifacts remain at ${plan.operationRoot}`);
915
1313
  }
@@ -923,7 +1321,8 @@ export async function applyGlobalUpdate(options, run = defaultRunner, execution
923
1321
  rollbackPerformed: true,
924
1322
  rollback: { status: 'healthy' },
925
1323
  error: safeUpdateError(updateError),
926
- events,
1324
+ events: events.events,
1325
+ eventTimes: events.eventTimes,
927
1326
  });
928
1327
  throw new Error('global update failed; the prior exact packages were restored and are healthy', { cause: updateError });
929
1328
  }
@@ -931,6 +1330,22 @@ export async function applyGlobalUpdate(options, run = defaultRunner, execution
931
1330
  function output(value, json) {
932
1331
  process.stdout.write(`${JSON.stringify(value, null, json ? 0 : 2)}\n`);
933
1332
  }
1333
+ const defaultRecoveryCommandDependencies = {
1334
+ dispatch: dispatchGlobalUpdateRecovery,
1335
+ recover: recoverGlobalUpdate,
1336
+ readPlan: readGlobalUpdatePlan,
1337
+ validateExecutor: validateCandidateExecutorInvocation,
1338
+ emit: output,
1339
+ };
1340
+ export async function runGlobalUpdateRecoveryCommand(options, dependencies = defaultRecoveryCommandDependencies) {
1341
+ if (typeof options.executorReceipt === 'string') {
1342
+ const plan = dependencies.readPlan(options.plan);
1343
+ dependencies.validateExecutor(plan, options.executorReceipt);
1344
+ dependencies.emit(await dependencies.recover(options), options.json === true);
1345
+ return;
1346
+ }
1347
+ dependencies.emit(dependencies.dispatch(options), options.json === true);
1348
+ }
934
1349
  export function makeGlobalUpdateCommand() {
935
1350
  const command = new Command('update').description('Check, prepare, and apply a guarded global AOPS npm update');
936
1351
  command
@@ -960,6 +1375,16 @@ export function makeGlobalUpdateCommand() {
960
1375
  }
961
1376
  output(dispatchGlobalUpdate(options), options.json === true);
962
1377
  });
1378
+ command
1379
+ .command('recover')
1380
+ .description('Finish one stranded exact plan at its target closure after a failed apply')
1381
+ .requiredOption('--plan <path>', 'Exact update-plan.json path returned by prepare')
1382
+ .requiredOption('--confirm <plan-id>', 'Exact sha256 plan id returned by prepare')
1383
+ .addOption(new Option('--executor-receipt <path>').hideHelp())
1384
+ .option('--json', 'Output JSON')
1385
+ .action(async (options) => {
1386
+ await runGlobalUpdateRecoveryCommand(options);
1387
+ });
963
1388
  command
964
1389
  .command('bridge')
965
1390
  .description('Apply an old-CLI plan from an externally launched exact public candidate')
@@ -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 "8829e388a6594a397d3177b17cc67f390cb37902e6d297136cbdc67a6cd44371" === 'undefined'
546
+ return typeof "99724da24c7a8af713cd05f83646af0ddbf95c89fc892ce31f0edc7d21874483" === 'undefined'
547
547
  ? undefined
548
- : "8829e388a6594a397d3177b17cc67f390cb37902e6d297136cbdc67a6cd44371";
548
+ : "99724da24c7a8af713cd05f83646af0ddbf95c89fc892ce31f0edc7d21874483";
549
549
  }
550
550
  function sha256(bytes) {
551
551
  return createHash('sha256').update(bytes).digest('hex');
@@ -205,6 +205,71 @@ function terminalObservation(record, observedAt, disappeared = false) {
205
205
  signal: record.signal ?? null,
206
206
  };
207
207
  }
208
+ export const COCKPIT_PID_START_TOLERANCE_MS = 300_000;
209
+ export function classifyTerminalCockpitPids(params) {
210
+ if (params.identityPresent) {
211
+ return { stale: false, reason: 'community_cockpit_terminal_identity_file_present', findings: [] };
212
+ }
213
+ if (params.livePids.length === 0) {
214
+ return { stale: false, reason: 'community_cockpit_terminal_no_live_pid', findings: [] };
215
+ }
216
+ const recordedStart = Date.parse(params.record.startedAt);
217
+ if (!Number.isFinite(recordedStart)) {
218
+ return { stale: false, reason: 'community_cockpit_pid_record_start_unparsable', findings: [] };
219
+ }
220
+ const tolerance = params.startToleranceMs ?? COCKPIT_PID_START_TOLERANCE_MS;
221
+ const findings = [];
222
+ let blocked = null;
223
+ for (const { pid, role } of params.livePids) {
224
+ let inspection;
225
+ try {
226
+ inspection = params.inspect(pid);
227
+ }
228
+ catch (error) {
229
+ inspection = {
230
+ outcome: 'undetermined',
231
+ pid,
232
+ reason: error instanceof Error ? error.message : 'community_cockpit_pid_inspection_threw',
233
+ };
234
+ }
235
+ if (inspection.outcome === 'absent') {
236
+ findings.push({ pid, role, outcome: 'absent', disagreedOn: ['identity', 'start-time', 'host-entry'], reason: null });
237
+ continue;
238
+ }
239
+ if (inspection.outcome === 'undetermined') {
240
+ findings.push({ pid, role, outcome: 'undetermined', disagreedOn: [], reason: inspection.reason });
241
+ blocked ??= `community_cockpit_pid_inspection_undetermined:${pid}:${inspection.reason}`;
242
+ continue;
243
+ }
244
+ const observedStart = Date.parse(inspection.startedAt);
245
+ if (!Number.isFinite(observedStart)) {
246
+ findings.push({ pid, role, outcome: 'inspected', disagreedOn: [], reason: 'start_time_unparsable' });
247
+ blocked ??= `community_cockpit_pid_start_time_unparsable:${pid}`;
248
+ continue;
249
+ }
250
+ if (inspection.command.trim() === '') {
251
+ findings.push({ pid, role, outcome: 'inspected', disagreedOn: [], reason: 'command_unavailable' });
252
+ blocked ??= `community_cockpit_pid_command_unavailable:${pid}`;
253
+ continue;
254
+ }
255
+ const disagreedOn = ['identity'];
256
+ if (Math.abs(observedStart - recordedStart) > tolerance)
257
+ disagreedOn.push('start-time');
258
+ if (!commandNamesHostEntry(inspection.command, params.record.hostEntry))
259
+ disagreedOn.push('host-entry');
260
+ findings.push({ pid, role, outcome: 'inspected', disagreedOn, reason: null });
261
+ if (disagreedOn.length !== 3) {
262
+ blocked ??= `community_cockpit_pid_may_be_owned:${pid}:${disagreedOn.join('+')}`;
263
+ }
264
+ }
265
+ if (blocked)
266
+ return { stale: false, reason: blocked, findings };
267
+ return { stale: true, reason: 'community_cockpit_terminal_record_pid_reused', findings };
268
+ }
269
+ function commandNamesHostEntry(command, hostEntry) {
270
+ const normalise = (value) => value.split('\\').join('/').toLowerCase();
271
+ return normalise(command).includes(normalise(hostEntry));
272
+ }
208
273
  async function observeCockpit(params) {
209
274
  const record = params.processRecord;
210
275
  const identityPresent = existsSync(params.paths.identityPath);
@@ -254,11 +319,40 @@ async function observeCockpit(params) {
254
319
  terminalObservation: terminalObservation(record, record.updatedAt),
255
320
  };
256
321
  }
322
+ const observation = terminalObservation(record, record.updatedAt);
323
+ const verdict = observation.classification === 'clean-exit'
324
+ ? classifyTerminalCockpitPids({
325
+ record,
326
+ identityPresent,
327
+ livePids: [
328
+ ...(supervisorAlive ? [{ pid: record.pid, role: 'supervisor' }] : []),
329
+ ...(hostAlive && hostPid ? [{ pid: hostPid, role: 'host' }] : []),
330
+ ],
331
+ inspect: params.runtime.inspectProcess ?? ((pid) => ({
332
+ outcome: 'undetermined',
333
+ pid,
334
+ reason: 'community_cockpit_pid_inspection_unavailable',
335
+ })),
336
+ })
337
+ : {
338
+ stale: false,
339
+ reason: `community_cockpit_terminal_record_with_live_or_unowned_process:${observation.classification}`,
340
+ findings: [],
341
+ };
342
+ if (verdict.stale) {
343
+ return {
344
+ ...base,
345
+ runtimeState: 'stopped', recoverable: true, process: record, identity,
346
+ supervisorAlive, hostAlive, health: 'not-checked',
347
+ reason: verdict.reason,
348
+ terminalObservation: observation,
349
+ };
350
+ }
257
351
  return {
258
352
  ...base,
259
353
  runtimeState: 'identity-conflict', recoverable: false, process: record, identity,
260
354
  supervisorAlive, hostAlive, health: 'not-checked',
261
- reason: 'community_cockpit_terminal_record_with_live_or_unowned_process',
355
+ reason: verdict.reason,
262
356
  terminalObservation: null,
263
357
  };
264
358
  }
@@ -1,5 +1,5 @@
1
1
  import { createHash, randomBytes, randomUUID } from 'node:crypto';
2
- import { spawn } from 'node:child_process';
2
+ import { spawn, spawnSync } from 'node:child_process';
3
3
  import { createRequire } from 'node:module';
4
4
  import { closeSync, existsSync, fsyncSync, lstatSync, mkdirSync, openSync, readdirSync, readSync, readFileSync, realpathSync, renameSync, rmSync, writeFileSync, } from 'node:fs';
5
5
  import path from 'node:path';
@@ -68,6 +68,56 @@ const PACKAGE_BUILD_PATHS = Object.freeze({
68
68
  handlerEntry: 'build/handler.js',
69
69
  cockpitIndex: 'cockpit/index.html',
70
70
  });
71
+ export function inspectHostProcess(pid, execute = (command, args) => {
72
+ const result = spawnSync(command, args, { encoding: 'utf8', windowsHide: true, timeout: 5_000 });
73
+ if (result.error)
74
+ throw result.error;
75
+ return { status: result.status, stdout: result.stdout ?? '', stderr: result.stderr ?? '' };
76
+ }, platform = process.platform) {
77
+ if (!Number.isSafeInteger(pid) || pid < 1) {
78
+ return { outcome: 'undetermined', pid, reason: 'pid_not_inspectable' };
79
+ }
80
+ try {
81
+ if (platform === 'win32') {
82
+ const result = execute('powershell.exe', [
83
+ '-NoProfile', '-NonInteractive', '-Command',
84
+ `$p = Get-CimInstance Win32_Process -Filter "ProcessId=${pid}" -ErrorAction Stop; ` +
85
+ 'if ($null -eq $p) { exit 3 }; ' +
86
+ '"{0}`t{1}" -f $p.CreationDate.ToString("o"), $p.CommandLine',
87
+ ]);
88
+ if (result.status === 3)
89
+ return { outcome: 'absent', pid };
90
+ if (result.status !== 0) {
91
+ return { outcome: 'undetermined', pid, reason: `process_query_failed:${result.status ?? 'no_status'}` };
92
+ }
93
+ const [startedAt, ...rest] = result.stdout.trim().split('\t');
94
+ if (!startedAt)
95
+ return { outcome: 'undetermined', pid, reason: 'process_query_empty' };
96
+ return { outcome: 'inspected', pid, startedAt, command: rest.join('\t').trim() };
97
+ }
98
+ const result = execute('ps', ['-o', 'lstart=,args=', '-p', String(pid)]);
99
+ if (result.status === 1 && result.stdout.trim() === '')
100
+ return { outcome: 'absent', pid };
101
+ if (result.status !== 0) {
102
+ return { outcome: 'undetermined', pid, reason: `process_query_failed:${result.status ?? 'no_status'}` };
103
+ }
104
+ const line = result.stdout.split('\n')[0]?.trim() ?? '';
105
+ if (line === '')
106
+ return { outcome: 'absent', pid };
107
+ const fields = line.split(/\s+/u);
108
+ if (fields.length < 6)
109
+ return { outcome: 'undetermined', pid, reason: 'process_query_unparsable' };
110
+ const startedAt = fields.slice(0, 5).join(' ');
111
+ return { outcome: 'inspected', pid, startedAt, command: fields.slice(5).join(' ') };
112
+ }
113
+ catch (error) {
114
+ return {
115
+ outcome: 'undetermined',
116
+ pid,
117
+ reason: error instanceof Error ? `process_query_threw:${error.message.slice(0, 80)}` : 'process_query_threw',
118
+ };
119
+ }
120
+ }
71
121
  function sha256(content) {
72
122
  return `sha256:${createHash('sha256').update(content).digest('hex')}`;
73
123
  }
@@ -1437,6 +1487,9 @@ export const communityNativeRuntime = {
1437
1487
  return Boolean(error && typeof error === 'object' && 'code' in error && error.code === 'EPERM');
1438
1488
  }
1439
1489
  },
1490
+ inspectProcess(pid) {
1491
+ return inspectHostProcess(pid);
1492
+ },
1440
1493
  sleep: (milliseconds, signal) => new Promise((resolve, reject) => {
1441
1494
  if (signal?.aborted) {
1442
1495
  reject(new Error('community_operation_aborted'));
@@ -6,7 +6,7 @@
6
6
  "platform": "darwin",
7
7
  "architecture": "arm64",
8
8
  "relativePath": "bin/darwin-arm64/aops-agent-assets-fs",
9
- "sha256": "8b51027fa9c2ae84480da09a48f5e1970b1b5444e65c79f2c95a84494af83aba",
9
+ "sha256": "aeff0f83ff7c53f1651ccee44d857f03c5a41846f30302433ade0f3705ae0940",
10
10
  "capabilityClass": "macos-exclusive-durable-v1",
11
11
  "qualification": null
12
12
  },
@@ -14,7 +14,7 @@
14
14
  "platform": "darwin",
15
15
  "architecture": "x64",
16
16
  "relativePath": "bin/darwin-x64/aops-agent-assets-fs",
17
- "sha256": "4e9023fe80b0c019c7d3b822efad79227a8185f6a546fc4a3671d72e4484dcd8",
17
+ "sha256": "d04f3e6ae34c2711cf1cf6bb8a6d5edb669cf4aed4c21e315b23e9d78a0799ef",
18
18
  "capabilityClass": "macos-exclusive-durable-v1",
19
19
  "qualification": null
20
20
  },
@@ -22,7 +22,7 @@
22
22
  "platform": "linux",
23
23
  "architecture": "arm64",
24
24
  "relativePath": "bin/linux-arm64/aops-agent-assets-fs",
25
- "sha256": "8e8457f64e9552a3094a910f3e2214ff2e309b8d2b4754af6fe30fd7fd674ff4",
25
+ "sha256": "4af380c99012b8e847570cb140c70c44c4dcdcee11c17cc5cb2bb8493b82dc8c",
26
26
  "capabilityClass": "linux-posix-durable-v1",
27
27
  "qualification": null
28
28
  },
@@ -30,7 +30,7 @@
30
30
  "platform": "linux",
31
31
  "architecture": "x64",
32
32
  "relativePath": "bin/linux-x64/aops-agent-assets-fs",
33
- "sha256": "a313faedbbb5e4ebd570750ce567ab7e42b23077a8ddf60a4c61ba24965dc01c",
33
+ "sha256": "1c36cd295f0fcfdfd4192748413483b099ac4299e0f770a982846b8915fba31d",
34
34
  "capabilityClass": "linux-posix-durable-v1",
35
35
  "qualification": null
36
36
  },
@@ -38,11 +38,11 @@
38
38
  "platform": "win32",
39
39
  "architecture": "x64",
40
40
  "relativePath": "bin/win32-x64/aops-agent-assets-fs.exe",
41
- "sha256": "9b54788cfec6108383d890fca1e79564258dfb9c8618f775d427b6ec371aac9d",
41
+ "sha256": "c1fcf2fe3689550f1552bdb60a064a9ac0c9e30cba4d9eb7c52140ee14794778",
42
42
  "capabilityClass": "windows-ntfs-crash-recoverable-v1",
43
43
  "qualification": {
44
44
  "relativePath": "qualifications/win32-x64.json",
45
- "sha256": "98d28b5ea98eab250ae1849e88dc729a5986916db9aafda8c463a8d366e8c0c2"
45
+ "sha256": "ef348d39e5c9e229b21274d00f779dbe9e8fd7bdbc27cb5c797191bb1294a5ae"
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": "5bd395ea8e18d402e82eaed62da75009ddc767e29f27847184e6099707a37b7d",
10
+ "evidenceSha256": "d58ca525d9e4a57c111747e1912e4d74266c712b127cfb9857b7d9d2beb5d3c1",
11
11
  "crashInjectionPoints": [
12
12
  "authority-history",
13
13
  "authority-current",
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aopslabs/aops",
3
- "version": "0.3.23",
3
+ "version": "0.3.24",
4
4
  "type": "module",
5
5
  "description": "AOPS CLI and terminal setup application.",
6
6
  "aopsDockerServerVersion": "0.2.17",
@@ -62,17 +62,17 @@
62
62
  },
63
63
  "dependencies": {
64
64
  "@aopslabs/aops-agent-runtime": "0.1.0",
65
- "@aopslabs/aops-server": "0.2.17",
65
+ "@aopslabs/aops-server": "0.2.18",
66
66
  "@aopslabs/artifact-trust-contracts": "0.3.1",
67
67
  "@aopslabs/cli-kit": "0.1.0",
68
- "@aopslabs/aops-host-registration": "0.2.1",
68
+ "@aopslabs/aops-host-registration": "0.2.2",
69
69
  "@aopslabs/aops-pg-bootstrap": "0.2.3",
70
- "@aopslabs/aops-runtime-config": "0.2.1",
70
+ "@aopslabs/aops-runtime-config": "0.2.2",
71
71
  "@sigstore/bundle": "4.0.0",
72
72
  "@sigstore/protobuf-specs": "0.5.1",
73
73
  "@sigstore/tuf": "4.0.2",
74
74
  "@sigstore/verify": "3.1.1",
75
- "@aopslabs/api-client": "0.2.1",
75
+ "@aopslabs/api-client": "0.2.2",
76
76
  "@aopslabs/domain-product-client-chatv3": "0.4.1",
77
77
  "@aopslabs/xf-cli-ui": "0.2.0",
78
78
  "commander": "14.0.3",